diff --git a/emmarin/cl/cl/tests/simple_transfer.rs b/emmarin/cl/cl/tests/simple_transfer.rs index 01a2cc8..4cad8a1 100644 --- a/emmarin/cl/cl/tests/simple_transfer.rs +++ b/emmarin/cl/cl/tests/simple_transfer.rs @@ -37,3 +37,4 @@ fn test_simple_transfer() { assert!(bundle.balance().is_zero()) } + diff --git a/emmarin/cl/ledger/src/stf.rs b/emmarin/cl/ledger/src/stf.rs index f416008..d826503 100644 --- a/emmarin/cl/ledger/src/stf.rs +++ b/emmarin/cl/ledger/src/stf.rs @@ -26,8 +26,38 @@ impl StfProof { pub fn stf(&self) -> Stf { risc0_constraint(self.risc0_id) } - pub fn verify(&self) -> bool { self.risc0_receipt.verify(self.risc0_id).is_ok() } + + pub fn prove_nop(public: StfPublic) -> Self { + let env = risc0_zkvm::ExecutorEnv::builder() + .write(&public) + .unwrap() + .build() + .unwrap(); + + let prover = risc0_zkvm::default_prover(); + + let start_t = std::time::Instant::now(); + + let opts = risc0_zkvm::ProverOpts::succinct(); + let prove_info = prover + .prove_with_opts(env, nomos_cl_risc0_proofs::STF_NOP_ELF, &opts) + .unwrap(); + + println!( + "STARK 'stf' prover time: {:.2?}, total_cycles: {}", + start_t.elapsed(), + prove_info.stats.total_cycles + ); + + let receipt = prove_info.receipt; + + Self { + risc0_id: nomos_cl_risc0_proofs::STF_NOP_ID, + public, + risc0_receipt: receipt, + } + } } diff --git a/emmarin/cl/ledger/src/zone_update.rs b/emmarin/cl/ledger/src/zone_update.rs index efcb251..fb145cd 100644 --- a/emmarin/cl/ledger/src/zone_update.rs +++ b/emmarin/cl/ledger/src/zone_update.rs @@ -10,47 +10,6 @@ pub struct ProvedUpdateBundle { } impl ProvedUpdateBundle { - // pub fn prove(bundle_witness: &BundleWitness) -> Result { - // // need to show that bundle is balanced. - // // i.e. the sum of ptx balances is 0 - - // let bundle_private = BundlePrivate { - // balances: bundle_witness - // .partials - // .iter() - // .map(|ptx| ptx.balance()) - // .collect(), - // }; - - // let env = risc0_zkvm::ExecutorEnv::builder() - // .write(&bundle_private) - // .unwrap() - // .build() - // .unwrap(); - - // let prover = risc0_zkvm::default_prover(); - - // let start_t = std::time::Instant::now(); - - // let opts = risc0_zkvm::ProverOpts::succinct(); - // let prove_info = prover - // .prove_with_opts(env, nomos_cl_risc0_proofs::BUNDLE_ELF, &opts) - // .map_err(|_| Error::Risc0ProofFailed)?; - - // println!( - // "STARK 'bundle' prover time: {:.2?}, total_cycles: {}", - // start_t.elapsed(), - // prove_info.stats.total_cycles - // ); - - // let receipt = prove_info.receipt; - - // Ok(Self { - // bundle: receipt.journal.decode()?, - // risc0_receipt: receipt, - // }) - // } - pub fn verify(&self) -> bool { let mut consumed_commitments = HashSet::new(); let mut produced_commitments = HashSet::new(); diff --git a/emmarin/cl/ledger/tests/simple_transfer.rs b/emmarin/cl/ledger/tests/simple_transfer.rs index dbccf05..8918fb6 100644 --- a/emmarin/cl/ledger/tests/simple_transfer.rs +++ b/emmarin/cl/ledger/tests/simple_transfer.rs @@ -1,17 +1,30 @@ use cl::{ cl::{ - note::derive_unit, BalanceWitness, BundleWitness, InputWitness, NoteWitness, + balance::Unit, note::derive_unit, BalanceWitness, BundleWitness, InputWitness, NoteWitness, NullifierCommitment, NullifierSecret, OutputWitness, PartialTxWitness, }, - zone_layer::ledger::LedgerWitness, + zone_layer::{ + ledger::LedgerWitness, + notes::ZoneNote, + tx::{UpdateBundle, ZoneUpdate}, + }, }; use ledger::{ bundle::ProvedBundle, constraint::ConstraintProof, ledger::{ProvedLedgerTransition, ProvedZoneTx}, partial_tx::ProvedPartialTx, + stf::StfProof, + zone_update::ProvedUpdateBundle, }; +use ledger_proof_statements::stf::StfPublic; use rand_core::CryptoRngCore; +use std::sync::OnceLock; + +fn nmo() -> &'static Unit { + static NMO: OnceLock = OnceLock::new(); + NMO.get_or_init(|| derive_unit("NMO")) +} struct User(NullifierSecret); @@ -33,63 +46,33 @@ fn receive_utxo(note: NoteWitness, nf_pk: NullifierCommitment) -> OutputWitness OutputWitness::new(note, nf_pk) } -#[test] -fn ledger_transition() { - let nmo = derive_unit("NMO"); - +fn cross_transfer_transition( + input: InputWitness, + to: User, + amount: u64, + zone_a: [u8; 32], + zone_b: [u8; 32], + ledger_a: LedgerWitness, + ledger_b: LedgerWitness, +) -> (ProvedLedgerTransition, ProvedLedgerTransition) { let mut rng = rand::thread_rng(); - - // alice is sending 8 NMO to bob. - - let alice = User::random(&mut rng); - let bob = User::random(&mut rng); - - // Alice has an unspent note worth 10 NMO - let utxo = receive_utxo( - NoteWitness::stateless(10, nmo, ConstraintProof::nop_constraint(), &mut rng), - alice.pk(), + assert!(amount <= input.note.value); + let change = input.note.value - amount; + let transfer = OutputWitness::new(NoteWitness::basic(amount, *nmo(), &mut rng), to.pk()); + let change = OutputWitness::new( + NoteWitness::basic(change, *nmo(), &mut rng), + input.nf_sk.commit(), ); - // Alice wants to send 8 NMO to bob - let bobs_output = OutputWitness::new(NoteWitness::basic(8, nmo, &mut rng), bob.pk()); - - let alice_change = OutputWitness::new(NoteWitness::basic(2, nmo, &mut rng), alice.pk()); - - let alice_input = InputWitness::from_output(utxo, alice.sk()); - - let zone_a = [0; 32]; - let zone_b = [1; 32]; - let ledger_a = LedgerWitness { - commitments: vec![utxo.commit_note(&zone_a)], - nullifiers: vec![], - }; - - let ledger_b = LedgerWitness { - commitments: vec![], - nullifiers: vec![], - }; - - let expected_ledger_a = LedgerWitness { - commitments: vec![utxo.commit_note(&zone_a), alice_change.commit_note(&zone_a)], - nullifiers: vec![alice_input.nullifier(&zone_a)], - }; - - let expected_ledger_b = LedgerWitness { - commitments: vec![bobs_output.commit_note(&zone_b)], - nullifiers: vec![], - }; - - // Construct the ptx consuming Alices inputs and producing the two outputs. - let alice_ptx_witness = PartialTxWitness { - inputs: vec![alice_input], - outputs: vec![bobs_output, alice_change], + // Construct the ptx consuming the input and producing the two outputs. + let ptx_witness = PartialTxWitness { + inputs: vec![input], + outputs: vec![transfer, change], balance_blinding: BalanceWitness::random_blinding(&mut rng), }; let proved_ptx = ProvedPartialTx::prove( - alice_ptx_witness.clone(), - vec![ledger_a - .cm_path(&alice_input.note_commitment(&zone_a)) - .unwrap()], + ptx_witness.clone(), + vec![ledger_a.cm_path(&input.note_commitment(&zone_a)).unwrap()], ledger_a.cm_root(), vec![zone_a], vec![zone_b, zone_a], @@ -97,12 +80,10 @@ fn ledger_transition() { .unwrap(); let bundle = ProvedBundle::prove(&BundleWitness { - partials: vec![alice_ptx_witness], + partials: vec![ptx_witness], }) .unwrap(); - assert_eq!(proved_ptx.cm_root, ledger_a.cm_root()); - let zone_tx = ProvedZoneTx { ptxs: vec![proved_ptx.clone()], bundle, @@ -110,7 +91,7 @@ fn ledger_transition() { // Prove the constraints for alices input (she uses the no-op constraint) let constraint_proof = - ConstraintProof::prove_nop(alice_input.nullifier(&zone_a), proved_ptx.ptx.root()); + ConstraintProof::prove_nop(input.nullifier(&zone_a), proved_ptx.ptx.root()); let ledger_a_transition = ProvedLedgerTransition::prove( ledger_a, @@ -123,6 +104,16 @@ fn ledger_transition() { let ledger_b_transition = ProvedLedgerTransition::prove(ledger_b, zone_b, vec![zone_tx], vec![]).unwrap(); + let expected_ledger_a = LedgerWitness { + commitments: vec![input.note_commitment(&zone_a), change.commit_note(&zone_a)], + nullifiers: vec![input.nullifier(&zone_a)], + }; + + let expected_ledger_b = LedgerWitness { + commitments: vec![transfer.commit_note(&zone_b)], + nullifiers: vec![], + }; + assert_eq!( ledger_a_transition.public.ledger, expected_ledger_a.commit() @@ -131,4 +122,101 @@ fn ledger_transition() { ledger_b_transition.public.ledger, expected_ledger_b.commit() ); + + (ledger_a_transition, ledger_b_transition) +} + +#[test] +fn zone_update_cross() { + let mut rng = rand::thread_rng(); + + // alice is sending 8 NMO to bob. + + let alice = User::random(&mut rng); + let bob = User::random(&mut rng); + + // Alice has an unspent note worth 10 NMO + let utxo = receive_utxo( + NoteWitness::stateless(10, *nmo(), ConstraintProof::nop_constraint(), &mut rng), + alice.pk(), + ); + + let alice_input = InputWitness::from_output(utxo, alice.sk()); + + let zone_a_id = [0; 32]; + let zone_b_id = [1; 32]; + + let ledger_a = LedgerWitness { + commitments: vec![utxo.commit_note(&zone_a_id)], + nullifiers: vec![], + }; + + let ledger_b = LedgerWitness { + commitments: vec![], + nullifiers: vec![], + }; + + let zone_a_old = ZoneNote { + id: zone_a_id, + state: [0; 32], + ledger: ledger_a.commit(), + stf: [0; 32], + }; + let zone_b_old = ZoneNote { + id: zone_b_id, + state: [0; 32], + ledger: ledger_b.commit(), + stf: [0; 32], + }; + + let (ledger_a_transition, ledger_b_transition) = cross_transfer_transition( + alice_input, + bob, + 8, + zone_a_id, + zone_b_id, + ledger_a, + ledger_b, + ); + + let zone_a_new = ZoneNote { + ledger: ledger_a_transition.public.ledger, + ..zone_a_old + }; + + let zone_b_new = ZoneNote { + ledger: ledger_b_transition.public.ledger, + ..zone_b_old + }; + + let stf_proof_a = StfProof::prove_nop(StfPublic { + old: zone_a_old, + new: zone_a_new, + }); + + let stf_proof_b = StfProof::prove_nop(StfPublic { + old: zone_b_old, + new: zone_b_new, + }); + + let update_bundle = UpdateBundle { + updates: vec![ + ZoneUpdate { + old: zone_a_old, + new: zone_a_new, + }, + ZoneUpdate { + old: zone_b_old, + new: zone_b_new, + }, + ], + }; + + let proved_bundle = ProvedUpdateBundle { + bundle: update_bundle, + ledger_proofs: vec![ledger_a_transition, ledger_b_transition], + stf_proofs: vec![stf_proof_a, stf_proof_b], + }; + + assert!(proved_bundle.verify()); } diff --git a/emmarin/cl/risc0_proofs/Cargo.toml b/emmarin/cl/risc0_proofs/Cargo.toml index 21cc296..e544320 100644 --- a/emmarin/cl/risc0_proofs/Cargo.toml +++ b/emmarin/cl/risc0_proofs/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" risc0-build = { version = "1.0" } [package.metadata.risc0] -methods = ["bundle", "constraint_nop", "ptx"] +methods = ["bundle", "constraint_nop", "ptx", "stf_nop"] diff --git a/emmarin/cl/risc0_proofs/stf_nop/Cargo.toml b/emmarin/cl/risc0_proofs/stf_nop/Cargo.toml new file mode 100644 index 0000000..ec1e0aa --- /dev/null +++ b/emmarin/cl/risc0_proofs/stf_nop/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "stf_nop" +version = "0.1.0" +edition = "2021" + +[workspace] + +[dependencies] +risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] } +serde = { version = "1.0", features = ["derive"] } +cl = { path = "../../cl" } +ledger_proof_statements = { path = "../../ledger_proof_statements" } + + +[patch.crates-io] +# add RISC Zero 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" } +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" } diff --git a/emmarin/cl/risc0_proofs/stf_nop/src/main.rs b/emmarin/cl/risc0_proofs/stf_nop/src/main.rs new file mode 100644 index 0000000..7c10c92 --- /dev/null +++ b/emmarin/cl/risc0_proofs/stf_nop/src/main.rs @@ -0,0 +1,8 @@ +/// Constraint No-op Proof +use ledger_proof_statements::stf::StfPublic; +use risc0_zkvm::guest::env; + +fn main() { + let public: StfPublic = env::read(); + env::commit(&public); +}