Cl: nullifier proof statement (#102)
* cl: nullifier proof * cl: cli action to select proof type
This commit is contained in:
parent
2c7c483707
commit
3d2459052c
|
@ -9,5 +9,13 @@ pub mod nullifier;
|
|||
pub mod output;
|
||||
pub mod partial_tx;
|
||||
|
||||
pub use balance::{Balance, BalanceWitness};
|
||||
pub use bundle::{Bundle, BundleWitness};
|
||||
pub use input::{Input, InputWitness};
|
||||
pub use note::{NoteCommitment, NoteWitness};
|
||||
pub use nullifier::{Nullifier, NullifierCommitment, NullifierNonce, NullifierSecret};
|
||||
pub use output::{Output, OutputWitness};
|
||||
pub use partial_tx::{PartialTx, PartialTxWitness, PtxRoot};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_util;
|
||||
|
|
|
@ -67,15 +67,14 @@ pub fn verify_path(leaf: [u8; 32], path: &[PathNode], root: [u8; 32]) -> bool {
|
|||
}
|
||||
|
||||
pub fn path<const N: usize>(leaves: [[u8; 32]; N], idx: usize) -> Vec<PathNode> {
|
||||
let n = leaves.len();
|
||||
assert!(n.is_power_of_two());
|
||||
assert!(idx < n);
|
||||
assert!(N.is_power_of_two());
|
||||
assert!(idx < N);
|
||||
|
||||
let mut nodes = leaves;
|
||||
let mut path = Vec::new();
|
||||
let mut idx = idx;
|
||||
|
||||
for h in (1..=n.ilog2()).rev() {
|
||||
for h in (1..=N.ilog2()).rev() {
|
||||
if idx % 2 == 0 {
|
||||
path.push(PathNode::Right(nodes[idx + 1]));
|
||||
} else {
|
||||
|
|
|
@ -9,6 +9,12 @@ use blake2::{Blake2s256, Digest};
|
|||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// TODO: create a nullifier witness and use it throughout.
|
||||
// struct NullifierWitness {
|
||||
// nf_sk: NullifierSecret,
|
||||
// nonce: NullifierNonce
|
||||
// }
|
||||
|
||||
// Maintained privately by note holder
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct NullifierSecret([u8; 16]);
|
||||
|
|
|
@ -30,9 +30,13 @@ impl OutputWitness {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn commit_note(&self) -> NoteCommitment {
|
||||
self.note.commit(self.nf_pk, self.nonce)
|
||||
}
|
||||
|
||||
pub fn commit(&self) -> Output {
|
||||
Output {
|
||||
note_comm: self.note.commit(self.nf_pk, self.nonce),
|
||||
note_comm: self.commit_note(),
|
||||
balance: self.note.balance(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
// 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 common::*;
|
||||
use cl::note::NoteWitness;
|
||||
use cl::input::InputWitness;
|
||||
use cl::output::OutputWitness;
|
||||
use cl::nullifier::NullifierSecret;
|
||||
use cl::partial_tx::{PartialTx, PartialTxWitness};
|
||||
use cl::merkle;
|
||||
use risc0_zkvm::{default_prover, ExecutorEnv};
|
||||
|
||||
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();
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
enum Action {
|
||||
Stf,
|
||||
Nullifier,
|
||||
}
|
||||
|
||||
fn stf_prove_stark() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let state: State = [(0, 1000)].into_iter().collect();
|
||||
|
@ -29,20 +26,24 @@ fn main() {
|
|||
|
||||
let in_state_cm = calculate_state_hash(&state);
|
||||
let in_journal_cm = calculate_journal_hash(&journal);
|
||||
let in_state_root = merkle::node(in_state_cm, in_journal_cm);
|
||||
let in_note = NoteWitness::new(1, "ZONE", in_state_root, &mut rng);
|
||||
let in_state_root = cl::merkle::node(in_state_cm, in_journal_cm);
|
||||
let in_note = cl::NoteWitness::new(1, "ZONE", in_state_root, &mut rng);
|
||||
|
||||
let mut out_journal = journal.clone();
|
||||
out_journal.push(zone_input);
|
||||
|
||||
let out_state_cm = calculate_state_hash(&stf(state.clone(), zone_input));
|
||||
let out_journal_cm = calculate_journal_hash(&out_journal);
|
||||
let out_state_root = merkle::node(out_state_cm, out_journal_cm);
|
||||
let out_note = NoteWitness::new(1, "ZONE", out_state_root, &mut rng);
|
||||
let out_state_root = cl::merkle::node(out_state_cm, out_journal_cm);
|
||||
let out_note = cl::NoteWitness::new(1, "ZONE", out_state_root, &mut rng);
|
||||
|
||||
let input = InputWitness::random(in_note, &mut rng);
|
||||
let output = OutputWitness::random(out_note, NullifierSecret::random(&mut rng).commit(), &mut rng);
|
||||
let ptx = PartialTx::from_witness(PartialTxWitness {
|
||||
let input = cl::InputWitness::random(in_note, &mut rng);
|
||||
let output = cl::OutputWitness::random(
|
||||
out_note,
|
||||
cl::NullifierSecret::random(&mut rng).commit(),
|
||||
&mut rng,
|
||||
);
|
||||
let ptx = cl::PartialTx::from_witness(cl::PartialTxWitness {
|
||||
inputs: vec![input.clone()],
|
||||
outputs: vec![output.clone()],
|
||||
});
|
||||
|
@ -78,14 +79,15 @@ fn main() {
|
|||
// Obtain the default prover.
|
||||
let prover = default_prover();
|
||||
|
||||
|
||||
use std::time::Instant;
|
||||
let start_t = Instant::now();
|
||||
|
||||
// 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::succinct();
|
||||
let prove_info = prover.prove_with_opts(env, METHOD_ELF, &opts).unwrap();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, methods::METHOD_ELF, &opts)
|
||||
.unwrap();
|
||||
|
||||
println!("STARK prover time: {:.2?}", start_t.elapsed());
|
||||
// extract the receipt.
|
||||
|
@ -96,7 +98,80 @@ fn main() {
|
|||
std::fs::write("proof.stark", bincode::serialize(&receipt).unwrap()).unwrap();
|
||||
// The receipt was verified at the end of proving, but the below code is an
|
||||
// example of how someone else could verify this receipt.
|
||||
receipt.verify(METHOD_ID).unwrap();
|
||||
receipt.verify(methods::METHOD_ID).unwrap();
|
||||
}
|
||||
|
||||
fn nf_prove_stark() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let nf_sk = cl::NullifierSecret::random(&mut rng);
|
||||
|
||||
let output = cl::OutputWitness {
|
||||
note: cl::NoteWitness {
|
||||
balance: cl::BalanceWitness::random(10, "NMO", &mut rng),
|
||||
death_constraint: vec![],
|
||||
state: [0u8; 32],
|
||||
},
|
||||
nf_pk: nf_sk.commit(),
|
||||
nonce: cl::NullifierNonce::random(&mut rng),
|
||||
};
|
||||
let output_cm = output.commit_note().as_bytes().to_vec();
|
||||
let cm_set = cl::merkle::padded_leaves::<64>(&[output_cm]);
|
||||
let cm_root = cl::merkle::root(cm_set);
|
||||
let cm_path = cl::merkle::path(cm_set, 0);
|
||||
let nf = cl::Nullifier::new(nf_sk, output.nonce);
|
||||
|
||||
let env = ExecutorEnv::builder()
|
||||
.write(&cm_root)
|
||||
.unwrap()
|
||||
.write(&nf)
|
||||
.unwrap()
|
||||
.write(&nf_sk)
|
||||
.unwrap()
|
||||
.write(&output)
|
||||
.unwrap()
|
||||
.write(&cm_path)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Obtain the default prover.
|
||||
let prover = default_prover();
|
||||
|
||||
use std::time::Instant;
|
||||
let start_t = Instant::now();
|
||||
|
||||
// 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::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, methods::NULLIFIER_ELF, &opts)
|
||||
.unwrap();
|
||||
|
||||
println!("STARK prover time: {:.2?}", start_t.elapsed());
|
||||
// extract the receipt.
|
||||
let receipt = prove_info.receipt;
|
||||
|
||||
// TODO: Implement code for retrieving receipt journal here.
|
||||
|
||||
std::fs::write("proof.stark", bincode::serialize(&receipt).unwrap()).unwrap();
|
||||
// The receipt was verified at the end of proving, but the below code is an
|
||||
// example of how someone else could verify this receipt.
|
||||
receipt.verify(methods::NULLIFIER_ID).unwrap();
|
||||
}
|
||||
|
||||
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 action = Action::parse();
|
||||
|
||||
match action {
|
||||
Action::Stf => stf_prove_stark(),
|
||||
Action::Nullifier => nf_prove_stark(),
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_state_hash(state: &State) -> [u8; 32] {
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0.1" }
|
||||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["guest"]
|
||||
methods = ["guest", "nullifier"]
|
||||
|
|
|
@ -8,18 +8,17 @@ edition = "2021"
|
|||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0.1", default-features = false, features = ['std'] }
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
blake2 = "0.10"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1"
|
||||
common = { path = "../../common" }
|
||||
cl = { path = "../../../cl" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint
|
||||
# multiplication accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" }
|
||||
# k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "nullifier"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1"
|
||||
cl = { path = "../../../cl" }
|
||||
|
||||
[patch.crates-io]
|
||||
# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint
|
||||
# multiplication accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
# k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
|
@ -0,0 +1,57 @@
|
|||
/// Nullifier Proof
|
||||
///
|
||||
/// Our goal: prove the nullifier nf was derived from a note that had previously been committed to.
|
||||
///
|
||||
/// More formally, nullifier statement says:
|
||||
/// for public input `nf` (nullifier) and `root_cm` (root of merkle tree over commitment set).
|
||||
/// the prover has knowledge of `output = (note, nf_pk, nonce)`, `nf` and `path` s.t. that the following constraints hold
|
||||
/// 0. nf_pk = hash(nf_sk)
|
||||
/// 1. nf = hash(nonce||nf_sk)
|
||||
/// 2. note_cm = output_commitment(output)
|
||||
/// 3. verify_merkle_path(note_cm, root, path)
|
||||
use cl::merkle;
|
||||
use cl::nullifier::{Nullifier, NullifierSecret};
|
||||
use cl::output::OutputWitness;
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn execute(
|
||||
// public
|
||||
cm_root: [u8; 32],
|
||||
nf: Nullifier,
|
||||
// private
|
||||
nf_sk: NullifierSecret,
|
||||
output: OutputWitness,
|
||||
cm_path: Vec<merkle::PathNode>,
|
||||
) {
|
||||
eprintln!("start exec: {}", env::cycle_count());
|
||||
|
||||
assert_eq!(output.nf_pk, nf_sk.commit());
|
||||
eprintln!("output nullifier: {}", env::cycle_count());
|
||||
|
||||
assert_eq!(nf, Nullifier::new(nf_sk, output.nonce));
|
||||
eprintln!("nullifier: {}", env::cycle_count());
|
||||
|
||||
let cm_out = output.commit_note();
|
||||
eprintln!("out_cm: {}", env::cycle_count());
|
||||
|
||||
assert!(merkle::verify_path(
|
||||
merkle::leaf(cm_out.as_bytes()),
|
||||
&cm_path,
|
||||
cm_root
|
||||
));
|
||||
eprintln!("nullifier merkle path: {}", env::cycle_count());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// public input
|
||||
let cm_root: [u8; 32] = env::read();
|
||||
let nf: Nullifier = env::read();
|
||||
|
||||
// private input
|
||||
let nf_sk: NullifierSecret = env::read();
|
||||
let output: OutputWitness = env::read();
|
||||
let cm_path: Vec<merkle::PathNode> = env::read();
|
||||
|
||||
eprintln!("parse input: {}", env::cycle_count());
|
||||
execute(cm_root, nf, nf_sk, output, cm_path);
|
||||
}
|
Loading…
Reference in New Issue