refactor risc0 zone

This commit is contained in:
Giacomo Pasini 2024-06-20 19:04:31 +02:00
parent b2c0f7eda0
commit 507e1627d7
No known key found for this signature in database
GPG Key ID: FC08489D2D895D4B
4 changed files with 139 additions and 74 deletions

View File

@ -8,3 +8,5 @@ methods = { path = "../methods" }
risc0-zkvm = { version = "1.0.1" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
serde = "1.0"
blake2 = "0.10"
bincode = "1"

View File

@ -1,49 +1,60 @@
// These constants represent the RISC-V ELF and the image ID generated by risc0-build.
// The ELF is used for proving and the ID is used for verification.
use blake2::{Blake2s256, Digest};
use methods::{METHOD_ELF, METHOD_ID};
use risc0_zkvm::{default_prover, ExecutorEnv};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
type Note = BTreeMap<u32, u32>;
// state of the zone
type State = BTreeMap<u32, u32>;
// list of all inputs that were executed up to this point
type Journal = Vec<Input>;
#[derive(Clone, Serialize, Deserialize)]
struct Note {
state_cm: [u8; 32],
journal_cm: [u8; 32],
zone_input: Input,
}
#[derive(Clone, Copy, Serialize, Deserialize)]
enum Input {
Transfer { from: u32, to: u32, amount: u32 },
}
fn main() {
// Initialize tracing. In order to view logs, run `RUST_LOG=info cargo run`
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::filter::EnvFilter::from_default_env())
.init();
let in_note: BTreeMap<u32, u32> = [(0, 1000)].into_iter().collect();
let in_note_cm = calculate_note_hash(&in_note);
let state: BTreeMap<u32, u32> = [(0, 1000)].into_iter().collect();
let journal = vec![];
let out_note: BTreeMap<u32, u32> = [(0, 990), (1, 10)].into_iter().collect();
let out_note_cm = calculate_note_hash(&out_note);
let note = Note {
state_cm: calculate_state_hash(&state),
journal_cm: calculate_journal_hash(&journal),
zone_input: Input::Transfer {
from: 0,
to: 1,
amount: 10,
},
};
let ptx_root = [0u8; 32];
let in_ptx_path: Vec<[u8; 32]> = vec![[0; 32]];
let out_ptx_path: Vec<[u8; 32]> = vec![[0; 32]];
println!("Before: {:?}", in_note);
let from = 0;
let to = 1;
let amount: u32 = 10;
let env = ExecutorEnv::builder()
.write(&ptx_root)
.unwrap()
.write(&in_ptx_path)
.unwrap()
.write(&out_ptx_path)
.write(&note)
.unwrap()
.write(&in_note_cm)
.write(&state)
.unwrap()
.write(&out_note_cm)
.unwrap()
.write(&from)
.unwrap()
.write(&to)
.unwrap()
.write(&amount)
.unwrap()
.write(&in_note)
.write(&journal)
.unwrap()
.build()
.unwrap();
@ -53,8 +64,7 @@ fn main() {
// Proof information by proving the specified ELF binary.
// This struct contains the receipt along with statistics about execution of the guest
let opts =
risc0_zkvm::ProverOpts::default().with_receipt_kind(risc0_zkvm::ReceiptKind::Groth16);
let opts = risc0_zkvm::ProverOpts::default();
let prove_info = prover.prove_with_opts(env, METHOD_ELF, &opts).unwrap();
// extract the receipt.
@ -63,7 +73,7 @@ fn main() {
// TODO: Implement code for retrieving receipt journal here.
// For example:
// let output: BTreeMap<u32, u32> = receipt.journal.decode().unwrap();
let (state_cm, journal_cm): ([u8; 32], [u8; 32]) = receipt.journal.decode().unwrap();
// println!("After: {:?}", output);
// The receipt was verified at the end of proving, but the below code is an
@ -71,8 +81,12 @@ fn main() {
receipt.verify(METHOD_ID).unwrap();
}
fn calculate_note_hash(n: &Note) -> [u8; 32] {
let mut out = [0u8; 32];
out[0] = n.len() as u8;
out
fn calculate_state_hash(state: &State) -> [u8; 32] {
let bytes = bincode::serialize(state).unwrap();
Blake2s256::digest(&bytes).into()
}
fn calculate_journal_hash(journal: &Journal) -> [u8; 32] {
let bytes = bincode::serialize(journal).unwrap();
Blake2s256::digest(&bytes).into()
}

View File

@ -7,4 +7,6 @@ edition = "2021"
[dependencies]
risc0-zkvm = { version = "1.0.1", default-features = false, features = ['std'] }
blake2 = "0.10"
blake2 = "0.10"
serde = { version = "1.0", features = ["derive"] }
bincode = "1"

View File

@ -1,61 +1,108 @@
use blake2::{Blake2s256, Digest};
use risc0_zkvm::guest::env;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// Public Inputs:
/// * ptx_root: the root of the partial tx merkle tree of inputs/outputs
/// * in_note_cm: a commitment to the input note
/// * out_note_cm: a commitment to the output note
/// Private inputs:
/// * in_note: a note corresponding to the input commitment
/// * in_ptx_path: the path from a leaf containing the input state commitment in the ptx
/// * out_ptx_path: the path from a leaf containing the output state commitment in the ptx
/// * from: u32, the account to transfer from
/// * to: u32, the account to transfer to
/// * amount: u32, the amount to transfer
///
type Note = BTreeMap<u32, u32>;
/// TODO
// state of the zone
type State = BTreeMap<u32, u32>;
// list of all inputs that were executed up to this point
type Journal = Vec<Input>;
#[derive(Clone, Serialize, Deserialize)]
struct Note {
state_cm: [u8; 32],
journal_cm: [u8; 32],
zone_input: Input,
}
#[derive(Clone, Copy, Serialize, Deserialize)]
enum Input {
Transfer { from: u32, to: u32, amount: u32 },
}
/// State transition function
fn stf(mut state: State, input: Input) -> State {
match input {
Input::Transfer { from, to, amount } => {
// compute transfer
let from = state.entry(from).or_insert(0);
*from = from.checked_sub(amount).unwrap();
*state.entry(to).or_insert(0) += amount;
}
}
state
}
/// Glue the zone and the cl together, specifically, it verifies the note requesting
/// a transfer is included as part of the same transaction in the cl
fn verify_ptx_inputs(ptx_root: [u8; 32], ptx_path: &[[u8; 32]], note: &Note) {
assert!(verify_path(&ptx_root, &ptx_path, &note));
}
/// Glue the zone and the cl together, specifically, it verifies an output note
/// containing the zone state is included as part of the same transaction in the cl
/// (this is done in the death condition to disallow burning)
fn verify_ptx_output(ptx_root: [u8; 32], ptx_path: &[[u8; 32]], note: &Note) {
assert!(verify_path(&ptx_root, &ptx_path, &note));
}
fn execute(
ptx_root: [u8; 32],
ptx_path: Vec<[u8; 32]>,
note: Note,
state: State,
mut journal: Journal,
) -> (State, Journal) {
// verify ptx/cl preconditions
verify_ptx_inputs(ptx_root, &ptx_path, &note);
// check the commitments match the actual data
let state_cm = calculate_state_hash(&state);
let journal_cm = calculate_journal_hash(&journal);
assert_eq!(state_cm, note.state_cm);
assert_eq!(journal_cm, note.journal_cm);
// then run the state transition function
let input = note.zone_input;
let state = stf(state, input);
journal.push(input);
let state_cm = calculate_state_hash(&state);
let journal_cm = calculate_journal_hash(&journal);
// verifying ptx/cl postconditions
verify_ptx_outputs(ptx_root, &ptx_path, out_note);
// output the new state and the execution receipt
(state, journal)
}
fn main() {
// public input
let ptx_root: [u8; 32] = env::read();
let in_ptx_path: Vec<[u8; 32]> = env::read();
let out_ptx_path: Vec<[u8; 32]> = env::read();
let in_note_cm: [u8; 32] = env::read();
let out_note_cm: [u8; 32] = env::read();
let from: u32 = env::read();
let to: u32 = env::read();
let amount: u32 = env::read();
// private input
let in_note: Note = env::read();
let in_ptx_path: Vec<[u8; 32]> = env::read();
let out_ptx_path: Vec<[u8; 32]> = env::read();
let note: Note = env::read();
let state: State = env::read();
let journal: Journal = env::read();
// check the note is consistent with the state commitment and is part of the path
assert_eq!(in_note_cm, calculate_note_hash(&in_note));
// verify the input state commitment is part of the partial tx
assert!(verify_path(&ptx_root, &in_ptx_path, &in_note));
// the note is just the state
let mut state = in_note.clone();
// compute transfer
let from = state.entry(from).or_insert(0);
*from = from.checked_sub(amount).unwrap();
*state.entry(to).or_insert(0) += amount;
// check that the new state is consistent with the output state commitment and it's part of the output
assert_eq!(calculate_note_hash(&state), out_note_cm);
let out_note = state;
assert!(verify_path(&ptx_root, &out_ptx_path, &out_note));
execute(ptx_root, in_ptx_path, out_ptx_path, note, state, journal);
}
fn calculate_note_hash(n: &Note) -> [u8; 32] {
let mut out = [0u8; 32];
out[0] = n.len() as u8;
out
fn calculate_state_hash(state: &State) -> [u8; 32] {
let bytes = bincode::serialize(state).unwrap();
Blake2s256::digest(&bytes).into()
}
fn calculate_journal_hash(journal: &Journal) -> [u8; 32] {
let bytes = bincode::serialize(journal).unwrap();
Blake2s256::digest(&bytes).into()
}
fn verify_path(_ptx_root: &[u8; 32], _ptx_path: &[[u8; 32]], _note: &Note) -> bool {