1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# Guest programs
This directory contains all programs which can be executed/proven/verified by a zkVM.
These are normal Rust programs, with certain specific patterns implemented for zkVM compatibility.
## Adding your own program
> [!IMPORTANT]
> **Fully implement and test your program before trying to add it here!**
> Although this repo makes it easy to run your program on all zkVMs, developing is not made easier.
1. **Copy the project to this directory.**
If you refer to local path dependencies, which you want to include with the project, move them inside the `guest/YOUR_PROJECT/` directory!
Everything directly inside `guests` is expected to be a crate, setup to be ran by the zkVMs.
2. **Convert your project into a library.**
You'll mainly need to rename `src/main.rs` to `src/lib.rs`.
3. **Update your `Cargo.toml`**
Add the `guests_macro` [dependency](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html) **exactly like this**:
```toml
guests_macro = { version = "0.1.0", path = "../../guests_macro" }
```
and add a `no_std` and zkVM [features](https://doc.rust-lang.org/cargo/reference/features.html) like this:
```toml
no_std = []
jolt = []
nexus = []
risc0 = []
sp1 = []
zkm = []
zkwasm = []
```
It's ok if you don't use those features in any way, however they **must** exist!
4. **Update your program.**
1. Above your main (entrypoint) function add a `#[guests_macro::proving_entrypoint]` attribute
2. Make sure the function is public
3. Move all input variable declarations as arguments to the main function and remove their assignments.
Input parsing is built-in and automagically handles types.
4. In case your program works without the standard library, or you want to support lack of std, remember to add
`#![cfg_attr(feature = "no_std", no_std)]` at the top of your `lib.rs`
So, if you have something like:
```rust
fn main() {
let n: u8 = read!();
...
}
```
transform it to:
```rust
#[guests_macro::proving_entrypoint]
pub fn main(n: u8) {
...
}
```
The entrypoint function does not need to be called `main` (the project is now a library).
5. **Add or update a `Cargo.lock`.**
Using any version of cargo, you simply need to do:
```sh
cargo generate-lockfile
```
6. **Add the "default" files.**
Three additional files **must** exist in your `guests/YOUR_PROJECT/` directory, containing default values: `default.env`, `default_private_input.toml`, `default_public_input.toml`.
Depending on your situation, it may be admissible to have any of these empty, but they **must** exist!
1. The first, `default.env`, contains pairs of `NAME=VALUE` and line comments, similar to shell scripts.
These values set zkVM-specific options.
Information on the possible values, their meaning and how to choose them, can be found [here](../zkvms/README.md#zkvm-specific-environment-variables).
However, generally, you'll only need to set ZKM's `SEG_SIZE`.
A simple example comes from `fibonacci`:
```sh
# ZKM
SEG_SIZE=2783
```
2. `default_private_input.toml` and `default_public_input.toml` contain default input values for your program.
Each [key](https://toml.io/en/v1.0.0#keyvalue-pair) is the name of an attribute in your main function.
Keys between the two files must be unique, meaning each main function attribute is defined in **only one** of the files.
Whether an input is public or not is defined in these files, **not** in your code!
It is preferable for your default input to be short, so the default execution, proving and verification steps are fast.
Again, simple examples are found in `fibonacci`.
For the following main function:
```rust
/* ... */
pub fn main(n: u8, fN: u64) -> bool {
/* ... */
```
this default private input:
```toml
fN = 259695496911122585
```
and this default public input:
```toml
n = 85
```
are correct.
7. **Track or commit your project with git.**
Due to the way Nix works, you'll need to at least track your guest program (but you probably should commit it).
```sh
git add guests/YOUR_PROJECT
```
## Using a program
You may execute/prove/verify a program in this directory (when the repository is cloned) by issuing:
```sh
nix run .#YOUR_PROJECT prove
```
It is highly possible your program will not be compatible with a certain zkVM.
You may either fix the issue, or blacklist the zkVM.
## Blacklisting
You may add files in the form `.no_ZKVM` to the `guest/YOUR_PROJECT` directory.
The existance of one will disallow building your guest program with the zkVM.
You'll still get an output from the Nix package, however it will be a trivial script, which prints a warning message.
If `.no_ZKVM` contains text, the contents will be printed alongside the warning.
|