|
1 | 1 | # Getting Started |
2 | 2 |
|
3 | 3 | We don't currently release Zirgen in any packaged form, so it's only available |
4 | | -through this repository. Assuming you've cloned and built things from this repo |
5 | | -before, building Zirgen with Bazel is simple with the following command. Note, |
6 | | -though, that this isn't strictly necessary, and that Bazel will automatically |
7 | | -(re)build Zirgen if you use it to invoke the tests as well. |
| 4 | +through this repository. This guide assumes you're using Bazel as your build |
| 5 | +system, and want to use Zirgen "out of tree" from a separate project. |
8 | 6 |
|
| 7 | + |
| 8 | +# Project structure |
| 9 | + |
| 10 | +## Pulling in the Zirgen compiler |
| 11 | + |
| 12 | +From a fresh project directory, we're going to need to create three build |
| 13 | +configuration files. First, create a `.bazelrc` file, and put the following two |
| 14 | +lines in it. These are necessary to ensure MLIR, one of Zirgen's dependencies, |
| 15 | +compiles correctly. |
9 | 16 | ``` |
10 | | -bazel build //zirgen/dsl:zirgen |
| 17 | +build --cxxopt=-std=c++17 |
| 18 | +build --host_cxxopt=-std=c++17 |
11 | 19 | ``` |
12 | 20 |
|
13 | | -## Hello world! |
| 21 | +Second, we need to pin our Bazel version. Zirgen is currently built with Bazel |
| 22 | +6.0, so we recommend creating a `.bazelversion` file with the following content. |
| 23 | +If you want to use a different version of Bazel for any reason, your mileage may |
| 24 | +vary. |
| 25 | +``` |
| 26 | +6.0.0 |
| 27 | +``` |
14 | 28 |
|
15 | | -Following in the footsteps of our forebears, let's take a look at a classic |
16 | | -"Hello world" program. This program is already available as a test and example, |
17 | | -so we can run it with the following command from the root of this repository: |
| 29 | +Third, create a `WORKSPACE` file. This is a Bazel configuration file that deals |
| 30 | +with "global" configurations like project dependencies, and this is where we're |
| 31 | +going to define how to pull in the Zirgen compiler: |
| 32 | +``` |
| 33 | +workspace(name = "zirgen-oot") |
| 34 | +
|
| 35 | +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") |
| 36 | +
|
| 37 | +git_repository( |
| 38 | + name = "zirgen", |
| 39 | + branch = "main", # feel free to pin a particular commit instead for stability! |
| 40 | + remote = "https://github.com/risc0/zirgen.git", |
| 41 | +) |
| 42 | +
|
| 43 | +load("@zirgen//bazel/rules/zirgen:deps.bzl", "zirgen_dependencies") |
| 44 | +zirgen_dependencies() |
| 45 | +
|
| 46 | +# configure transitive dependencies |
| 47 | +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") |
| 48 | +bazel_skylib_workspace() |
| 49 | +
|
| 50 | +load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure") |
| 51 | +llvm_configure(name = "llvm-project") |
| 52 | +``` |
18 | 53 |
|
| 54 | +At this point, it should be possible to build the Zirgen compiler, which might |
| 55 | +take a few minutes but only needs to be done once: |
19 | 56 | ``` |
20 | | -$ bazel run //zirgen/dsl:zirgen -- $(pwd)/zirgen/dsl/test/hello_world.zir --test |
21 | | -... |
22 | | -Running 0 |
23 | | -[0] Log: Hello world! |
24 | | -Lookups resolved |
| 57 | +bazel build @zirgen//zirgen/dsl:zirgen |
25 | 58 | ``` |
26 | 59 |
|
27 | | -This command passes two arguments to the Zirgen executable. The first, |
28 | | -`$(pwd)/zirgen/dsl/test/hello_world.zir`, specifies the path of the Zirgen file |
29 | | -we want to run on. The second, `--test`, specifies that we want to run all the |
30 | | -tests defined in the file we're running. Typically, the output of Zirgen is a |
31 | | -generated Rust or C++ library that then needs to be integrated with the RISC |
32 | | -Zero proof system. For the sake of simplicity here and as a useful practice |
33 | | -during the development, it is easiest to experiment with Zirgen by writing tests |
34 | | -alongside your circuit code, which can be run in the builtin interpreter without |
35 | | -doing this integration work using the `--test` option. Now, the important part |
36 | | -of the file is the following: |
| 60 | +## Setting up your new circuit |
37 | 61 |
|
| 62 | +Now that the compiler is set up, we just need to start the new circuit. Create a |
| 63 | +new directory called `circuit`; the name is not important, but consistency is |
| 64 | +key! In this directory, create a new file called `circuit.zir`, and include the |
| 65 | +following code: |
38 | 66 | ``` |
39 | | -test { |
40 | | - Log("Hello world!"); |
| 67 | +test Hello { |
| 68 | + Log("Hello world!"); |
41 | 69 | } |
42 | 70 | ``` |
43 | 71 |
|
44 | | -The keyword `test` declares that the thing that follows (enclosed in curly |
45 | | -braces) is a test. Tests can be given an optional name, but if they aren't named |
46 | | -then they are labeled with sequential numbers. This causes the text "Running 0" |
47 | | -to be written to stdout, marking the beginning of the execution of that test. |
48 | | -The statement `Log("Hello world!");` is what causes the text "[0] Log: Hello |
49 | | -world!" to be written to stdout. |
| 72 | +Next, add a `BUILD.bazel` file in the same directory, and add the following to |
| 73 | +it to configure a new build rule that generates Rust code for the circuit. |
| 74 | +``` |
| 75 | +load("@zirgen//bazel/rules/zirgen:dsl-defs.bzl", "zirgen_genfiles") |
| 76 | +
|
| 77 | +filegroup( |
| 78 | + name = "imports", |
| 79 | + srcs = glob(["*.zir"]), |
| 80 | +) |
| 81 | +
|
| 82 | +zirgen_genfiles( |
| 83 | + name = "CircuitIncs", |
| 84 | + zir_file = ":circuit.zir", |
| 85 | + data = [":imports"], |
| 86 | + zirgen_outs = [ |
| 87 | + ( |
| 88 | + ["--emit=rust"], |
| 89 | + "circuit.rs.inc", |
| 90 | + ), |
| 91 | + ], |
| 92 | +) |
| 93 | +``` |
| 94 | + |
| 95 | +## Hello world! |
| 96 | + |
| 97 | +Now with everything set up, we're ready to run the new circuit. The output of |
| 98 | +the Zirgen compiler is generated code that can then be called by 0STARK to |
| 99 | +generate real proofs. Our circuit is so trivial that there's not really much to |
| 100 | +generate, but it can be done with the following Bazel command, which will place |
| 101 | +the code in the Bazel build directory: |
| 102 | +``` |
| 103 | +bazel build //circuit:GenerateCircuitIncs |
| 104 | +``` |
| 105 | + |
| 106 | +Of more immediate interest, it's also possible to test and debug circuits using |
| 107 | +a built-in interpreter. This doesn't generate real proofs, but it does make it |
| 108 | +easy to try things out as you go along. To run our circuit in the interpreter: |
| 109 | +``` |
| 110 | +bazel run @zirgen//zirgen/dsl:zirgen -- $(pwd)/circuit/circuit.zir --test |
| 111 | +``` |
| 112 | +> ``` |
| 113 | +> Running Hello |
| 114 | +> [0] Log: Hello world! |
| 115 | +> Lookups resolved |
| 116 | +> Verifying constraints for Hello |
| 117 | +> Verifying zll constraints for Hello |
| 118 | +> ``` |
| 119 | +
|
| 120 | +This command invokes the Zirgen compiler through Bazel. Everything after `--` is |
| 121 | +passed directly to the `zirgen` executable as a command line option: the first |
| 122 | +argument indicates the "main" file of the circuit. The `--test` indicates that |
| 123 | +all the tests in the source code should be run in the interpreter. In this case, |
| 124 | +our code has one test named `Hello`, and it logs the string "Hello world!" on |
| 125 | +cycle zero. And presto! We've set up, written, and run a brand-new circuit! |
50 | 126 |
|
51 | 127 | [Prev](README.md) |
52 | 128 | [Next](02_Conceptual_Overview.md) |
0 commit comments