updated sp1 post 471f64ce
Steve · 2024-10-27 13:08 1 file(s) · +126 −3
src/content/post/how-sp1-precompiles-revolutionized-zkvm-performance.mdx +126 −3
64 64
- `lib` - This directory has some helper structs that we'll use in the program for deserializing data.
65 65
- `elf` - The `elf` directory holds a special ELF (Executable and Linkable Format) file that is used to make our program and script programs talk to each other.
66 66
67 +
Let's take a quick look at the program we'll be generating a proof for:
67 68
68 -
Now let's start testing. Run `cd program` to move into the program directory, and then run:
69 +
```rust
70 +
// These two lines are necessary for the program to properly compile.
71 +
//
72 +
// Under the hood, we wrap your main function with some extra code so that it behaves properly
73 +
// inside the zkVM.
74 +
#![no_main]
75 +
sp1_zkvm::entrypoint!(main);
76 +
77 +
use alloy_sol_types::{private::FixedBytes, SolType};
78 +
use precompiles_demo::PublicValuesStruct;
79 +
use tiny_keccak::{Hasher, Keccak};
80 +
//use patched_tiny_keccak::{Hasher, Keccak};
81 +
82 +
pub fn main() {
83 +
    // Read an input to the program.
84 +
    //
85 +
    // Behind the scenes, this compiles down to a custom system call which handles reading inputs
86 +
    // from the prover.
87 +
    let input = sp1_zkvm::io::read::<String>();
88 +
89 +
    // Compute a keccak hash form the input
90 +
    let mut hasher = Keccak::v256();
91 +
    hasher.update(input.as_bytes());
92 +
    let mut hash_bytes = [0u8; 32];
93 +
    hasher.finalize(&mut hash_bytes);
94 +
95 +
    let hash_fixed = FixedBytes::<32>(hash_bytes);
96 +
97 +
    // Encode the public values of the program.
98 +
    let bytes = PublicValuesStruct::abi_encode(&PublicValuesStruct {
99 +
        input,
100 +
        hash: hash_fixed,
101 +
    });
102 +
103 +
    // Commit to the public values of the program. The final proof will have a commitment to all the
104 +
    // bytes that were committed to.
105 +
    sp1_zkvm::io::commit_slice(&bytes);
106 +
}
107 +
```
108 +
109 +
At the top of our file we have an `entrypoint` for the `sp1_zkvm` which is needed to run inside SP1. Outside of that we have some basic Rust code that takes an input, creates a hash, and returns the input and the resulting hash. SP1 provides some utilities to handle things like `io` to read inputs as well as commiting them.
110 +
111 +
Now let's start testing. If you haven't already run `cd program` to move into the program directory, and then run:
69 112
70 113
```
71 114
cargo prove build
72 115
```
73 116
74 -
This will build our program using SP1 and prepare it for the next step, execution. Once the build is finished run the following command:
117 +
This will build our program using SP1 and prepare it for the next step, execution.
118 +
119 +
Inside our `script` folder we have a `/bin/main.rs` file that will run the program through SP1:
120 +
121 +
```rust
122 +
use alloy_sol_types::SolType;
123 +
use clap::Parser;
124 +
use precompiles_demo::PublicValuesStruct;
125 +
use sp1_sdk::{ProverClient, SP1Stdin};
126 +
127 +
/// The ELF (executable and linkable format) file for the Succinct RISC-V zkVM.
128 +
pub const ELF: &[u8] = include_bytes!("../../../elf/riscv32im-succinct-zkvm-elf");
129 +
130 +
/// The arguments for the command.
131 +
#[derive(Parser, Debug)]
132 +
#[clap(author, version, about, long_about = None)]
133 +
struct Args {
134 +
    #[clap(long)]
135 +
    execute: bool,
136 +
137 +
    #[clap(long)]
138 +
    prove: bool,
75 139
140 +
    #[clap(long, default_value = "Hello World from SP1!")]
141 +
    n: String,
142 +
}
143 +
144 +
fn main() {
145 +
    // Setup the logger.
146 +
    sp1_sdk::utils::setup_logger();
147 +
148 +
    // Parse the command line arguments.
149 +
    let args = Args::parse();
150 +
151 +
    if args.execute == args.prove {
152 +
        eprintln!("Error: You must specify either --execute or --prove");
153 +
        std::process::exit(1);
154 +
    }
155 +
156 +
    // Setup the prover client.
157 +
    let client = ProverClient::new();
158 +
159 +
    // Setup the inputs.
160 +
    let mut stdin = SP1Stdin::new();
161 +
    stdin.write(&args.n);
162 +
163 +
    println!("n: {}", args.n);
164 +
165 +
    if args.execute {
166 +
        // Execute the program
167 +
        let (output, report) = client.execute(ELF, stdin).run().unwrap();
168 +
        println!("Program executed successfully.");
169 +
170 +
        // Read the output.
171 +
        let decoded = PublicValuesStruct::abi_decode(output.as_slice(), true).unwrap();
172 +
        let PublicValuesStruct { input, hash } = decoded;
173 +
        println!("Input: {}", input);
174 +
        println!("Hash: 0x{}", hex::encode(hash.0)); // Convert bytes to hex string
175 +
176 +
        // Record the number of cycles executed.
177 +
        println!("Number of cycles: {}", report.total_instruction_count());
178 +
    } else {
179 +
        // Setup the program for proving.
180 +
        let (pk, vk) = client.setup(ELF);
181 +
182 +
        // Generate the proof
183 +
        let proof = client
184 +
            .prove(&pk, stdin)
185 +
            .run()
186 +
            .expect("failed to generate proof");
187 +
188 +
        println!("Successfully generated proof!");
189 +
190 +
        // Verify the proof.
191 +
        client.verify(&proof, &vk).expect("failed to verify proof");
192 +
        println!("Successfully verified proof!");
193 +
    }
194 +
}
76 195
```
77 -
cd ../script && cargo run --release -- --execute
196 +
197 +
This will look for arguments during `cargo run` to determin if it should only run an execution or if it should generate a proof. Executions are great for testing the code and will take less time and computation power than proofs. Once we have the arguments we can create an instance of the `ProverClient` to run our program and get the committed values back. Now lets try it out with the following command, making sure we have run `cd ../script` first to be in this directory instead of program:
198 +
199 +
```
200 +
cargo run --release -- --execute
78 201
```
79 202
80 203
This will start the bin command and will take a little time to run. Once complete you should see an output like this one: