diff --git a/emmarin/.gitignore b/emmarin/.gitignore new file mode 100644 index 0000000..6125671 --- /dev/null +++ b/emmarin/.gitignore @@ -0,0 +1 @@ +*profile.pb \ No newline at end of file diff --git a/emmarin/cl/Cargo.toml b/emmarin/cl/Cargo.toml index 836a57c..86795ca 100644 --- a/emmarin/cl/Cargo.toml +++ b/emmarin/cl/Cargo.toml @@ -1,6 +1,14 @@ [workspace] resolver = "2" -members = [ "cl", "ledger", "ledger_proof_statements", "risc0_proofs", "ledger_validity_proof"] +members = [ + "cl", + "ledger", + "ledger_proof_statements", + "risc0_proofs", + "bundle_risc0_proof", + "ptx_risc0_proof", + "ledger_validity_proof" +] # Always optimize; building and running the risc0_proofs takes much longer without optimization. [profile.dev] diff --git a/emmarin/cl/bundle_risc0_proof/Cargo.toml b/emmarin/cl/bundle_risc0_proof/Cargo.toml new file mode 100644 index 0000000..63e3d70 --- /dev/null +++ b/emmarin/cl/bundle_risc0_proof/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nomos_cl_bundle_risc0_proof" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +risc0-build = { version = "1.0" } + +[package.metadata.risc0] +methods = ["bundle"] + diff --git a/emmarin/cl/bundle_risc0_proof/build.rs b/emmarin/cl/bundle_risc0_proof/build.rs new file mode 100644 index 0000000..08a8a4e --- /dev/null +++ b/emmarin/cl/bundle_risc0_proof/build.rs @@ -0,0 +1,3 @@ +fn main() { + risc0_build::embed_methods(); +} diff --git a/emmarin/cl/risc0_proofs/balance/Cargo.toml b/emmarin/cl/bundle_risc0_proof/bundle/Cargo.toml similarity index 90% rename from emmarin/cl/risc0_proofs/balance/Cargo.toml rename to emmarin/cl/bundle_risc0_proof/bundle/Cargo.toml index 161f1c8..0903802 100644 --- a/emmarin/cl/risc0_proofs/balance/Cargo.toml +++ b/emmarin/cl/bundle_risc0_proof/bundle/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "balance" +name = "bundle" version = "0.1.0" edition = "2021" @@ -10,6 +10,7 @@ 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" } +nomos_cl_ptx_risc0_proof = { path = "../../ptx_risc0_proof" } [patch.crates-io] diff --git a/emmarin/cl/bundle_risc0_proof/bundle/src/main.rs b/emmarin/cl/bundle_risc0_proof/bundle/src/main.rs new file mode 100644 index 0000000..0473977 --- /dev/null +++ b/emmarin/cl/bundle_risc0_proof/bundle/src/main.rs @@ -0,0 +1,49 @@ +use cl::cl::BalanceWitness; +use cl::zone_layer::notes::ZoneId; +use ledger_proof_statements::bundle::{BundlePrivate, BundlePublic, LedgerUpdate}; +use risc0_zkvm::{guest::env, serde}; +use std::collections::BTreeMap; + +fn main() { + let bundle_private: BundlePrivate = env::read(); + let bundle_id = bundle_private.id(); + + let BundlePrivate { bundle, balances } = bundle_private; + assert_eq!(bundle.len(), balances.len()); + + let mut zone_ledger_updates: BTreeMap = BTreeMap::new(); + + for (ptx_public, balance) in bundle.into_iter().zip(balances.iter()) { + assert_eq!(ptx_public.ptx.balance, balance.commit()); + env::verify( + nomos_cl_ptx_risc0_proof::PTX_ID, + &serde::to_vec(&ptx_public).unwrap(), + ) + .unwrap(); + + for (input, cm_mmr) in ptx_public.ptx.inputs.iter().zip(ptx_public.cm_mmrs) { + let zone_ledger_update = zone_ledger_updates.entry(input.zone_id).or_default(); + + zone_ledger_update.nullifiers.push(input.nullifier); + + zone_ledger_update + .cm_roots + .extend(cm_mmr.roots.iter().map(|r| r.root)); + } + + for output in &ptx_public.ptx.outputs { + zone_ledger_updates + .entry(output.zone_id) + .or_default() + .commitments + .push(output.note_comm); + } + } + + assert!(BalanceWitness::combine(balances, [0u8; 16]).is_zero()); + + env::commit(&BundlePublic { + bundle_id, + zone_ledger_updates, + }); +} diff --git a/emmarin/cl/bundle_risc0_proof/src/lib.rs b/emmarin/cl/bundle_risc0_proof/src/lib.rs new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/emmarin/cl/bundle_risc0_proof/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/methods.rs")); diff --git a/emmarin/cl/cl/src/cl/bundle.rs b/emmarin/cl/cl/src/cl/bundle.rs index 4407ba9..5609c0b 100644 --- a/emmarin/cl/cl/src/cl/bundle.rs +++ b/emmarin/cl/cl/src/cl/bundle.rs @@ -1,45 +1,41 @@ use serde::{Deserialize, Serialize}; -use crate::{cl::partial_tx::PartialTx, zone_layer::notes::ZoneId}; -use sha2::{Digest, Sha256}; -use std::collections::HashSet; +use crate::cl::partial_tx::PartialTx; /// The transaction bundle is a collection of partial transactions. /// The goal in bundling transactions is to produce a set of partial transactions /// that balance each other. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct BundleId(pub [u8; 32]); - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Bundle { pub partials: Vec, } -impl Bundle { - pub fn zones(&self) -> HashSet { - self.partials - .iter() - .flat_map(|ptx| { - ptx.inputs - .iter() - .map(|i| i.zone_id) - .chain(ptx.outputs.iter().map(|o| o.zone_id)) - }) - .collect() - } +// impl Bundle { +// pub fn zones(&self) -> BTreeSet { +// self.partials +// .iter() +// .flat_map(|ptx| { +// ptx.inputs +// .iter() +// .map(|i| i.zone_id) +// .chain(ptx.outputs.iter().map(|o| o.zone_id)) +// }) +// .collect() +// } - pub fn id(&self) -> BundleId { - // TODO: change to merkle root - let mut hasher = Sha256::new(); - hasher.update(b"NOMOS_CL_BUNDLE_ID"); - for ptx in &self.partials { - hasher.update(ptx.root().0); - } +// // TODO: remove this +// pub fn id(&self) -> BundleId { +// // TODO: change to merkle root +// let mut hasher = Sha256::new(); +// hasher.update(b"NOMOS_CL_BUNDLE_ID"); +// for ptx in &self.partials { +// hasher.update(ptx.root().0); +// } - BundleId(hasher.finalize().into()) - } -} +// BundleId(hasher.finalize().into()) +// } +// } #[cfg(test)] mod test { diff --git a/emmarin/cl/cl/src/zone_layer/ledger.rs b/emmarin/cl/cl/src/zone_layer/ledger.rs index e01d49d..6ac6c72 100644 --- a/emmarin/cl/cl/src/zone_layer/ledger.rs +++ b/emmarin/cl/cl/src/zone_layer/ledger.rs @@ -27,7 +27,15 @@ impl LedgerWitness { } } - pub fn assert_nf_update(&mut self, nf: Nullifier, path: &[merkle::PathNode]) { + pub fn valid_cm_root(&self, root: [u8; 32]) -> bool { + self.commitments.roots.iter().any(|r| r.root == root) + } + + pub fn add_commitment(&mut self, cm: &NoteCommitment) { + self.commitments.push(&cm.0); + } + + pub fn assert_nf_update(&mut self, nf: &Nullifier, path: &[merkle::PathNode]) { // verify that the path corresponds to the nullifier assert_eq!(sparse_merkle::path_key(path), nf.0); @@ -57,7 +65,7 @@ impl LedgerState { sparse_merkle::sparse_root(&self.nullifiers) } - pub fn add_commitment(&mut self, cm: NoteCommitment) -> MMRProof { + pub fn add_commitment(&mut self, cm: &NoteCommitment) -> MMRProof { self.commitments.push(&cm.0) } diff --git a/emmarin/cl/ledger/Cargo.toml b/emmarin/cl/ledger/Cargo.toml index 053c813..6bde197 100644 --- a/emmarin/cl/ledger/Cargo.toml +++ b/emmarin/cl/ledger/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" cl = { path = "../cl" } ledger_proof_statements = { path = "../ledger_proof_statements" } nomos_cl_risc0_proofs = { path = "../risc0_proofs" } +nomos_cl_bundle_risc0_proof = { path = "../bundle_risc0_proof" } +nomos_cl_ptx_risc0_proof = { path = "../ptx_risc0_proof" } ledger_validity_proof = { path = "../ledger_validity_proof" } risc0-zkvm = { version = "1.0", features = ["prove", "metal"] } risc0-groth16 = { version = "1.0" } diff --git a/emmarin/cl/ledger/src/balance.rs b/emmarin/cl/ledger/src/balance.rs deleted file mode 100644 index d408782..0000000 --- a/emmarin/cl/ledger/src/balance.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::error::{Error, Result}; -use ledger_proof_statements::balance::{BalancePrivate, BalancePublic}; - -#[derive(Debug, Clone)] -pub struct ProvedBalance { - pub bundle: BalancePublic, - pub risc0_receipt: risc0_zkvm::Receipt, -} - -impl ProvedBalance { - pub fn prove(balance_witness: &BalancePrivate) -> Result { - //show that the sum of ptx balances is 0 - let env = risc0_zkvm::ExecutorEnv::builder() - .write(&balance_witness) - .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::BALANCE_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 public(&self) -> Result { - Ok(self.risc0_receipt.journal.decode()?) - } - - pub fn verify(&self) -> bool { - // let Ok(_bundle_public) = self.public() else { - // return false; - // }; - - // Vec::from_iter(self.bundle.partials.iter().map(|ptx| ptx.balance)) == bundle_public.balances - // && - self.risc0_receipt - .verify(nomos_cl_risc0_proofs::BALANCE_ID) - .is_ok() - } -} diff --git a/emmarin/cl/ledger/src/bundle.rs b/emmarin/cl/ledger/src/bundle.rs new file mode 100644 index 0000000..213f53b --- /dev/null +++ b/emmarin/cl/ledger/src/bundle.rs @@ -0,0 +1,52 @@ +use ledger_proof_statements::bundle::{BundlePrivate, BundlePublic}; + +use crate::partial_tx::ProvedPartialTx; + +#[derive(Debug, Clone)] +pub struct ProvedBundle { + pub risc0_receipt: risc0_zkvm::Receipt, +} + +impl ProvedBundle { + pub fn prove(bundle: &BundlePrivate, partials: Vec) -> Self { + //show that all ptx's are individually valid, and balance to 0 + let mut env = risc0_zkvm::ExecutorEnv::builder(); + + for proved_ptx in partials { + env.add_assumption(proved_ptx.risc0_receipt); + } + + let env = env.write(&bundle).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_bundle_risc0_proof::BUNDLE_ELF, &opts) + .unwrap(); + + println!( + "STARK 'bundle' prover time: {:.2?}, total_cycles: {}", + start_t.elapsed(), + prove_info.stats.total_cycles + ); + + let receipt = prove_info.receipt; + + Self { + risc0_receipt: receipt, + } + } + + pub fn public(&self) -> BundlePublic { + self.risc0_receipt.journal.decode().unwrap() + } + + pub fn verify(&self) -> bool { + self.risc0_receipt + .verify(nomos_cl_bundle_risc0_proof::BUNDLE_ID) + .is_ok() + } +} diff --git a/emmarin/cl/ledger/src/ledger.rs b/emmarin/cl/ledger/src/ledger.rs index cc126d3..1660d9d 100644 --- a/emmarin/cl/ledger/src/ledger.rs +++ b/emmarin/cl/ledger/src/ledger.rs @@ -1,80 +1,66 @@ -use ledger_proof_statements::ledger::{ - LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic, LedgerPtxWitness, -}; +use std::collections::BTreeMap; -use crate::{ - balance::ProvedBalance, - constraint::ConstraintProof, - error::{Error, Result}, - partial_tx::ProvedPartialTx, -}; +use ledger_proof_statements::ledger::{LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic}; + +use crate::bundle::ProvedBundle; use cl::zone_layer::{ledger::LedgerState, notes::ZoneId}; #[derive(Debug, Clone)] pub struct ProvedLedgerTransition { - pub public: LedgerProofPublic, pub risc0_receipt: risc0_zkvm::Receipt, } -// TODO: find a better name -#[derive(Debug, Clone)] -pub struct ProvedBundle { - pub balance: ProvedBalance, - pub ptxs: Vec, -} - -impl ProvedBundle { - fn proofs(&self) -> Vec { - let mut proofs = vec![self.balance.risc0_receipt.clone()]; - proofs.extend(self.ptxs.iter().map(|p| p.risc0_receipt.clone())); - proofs - } -} - impl ProvedLedgerTransition { - pub fn prove( - mut ledger: LedgerState, - zone_id: ZoneId, - bundles: Vec, - constraints: Vec, - ) -> Result { + pub fn prove(mut ledger: LedgerState, zone_id: ZoneId, bundles: Vec) -> Self { let mut witness = LedgerProofPrivate { bundles: Vec::new(), ledger: ledger.to_witness(), id: zone_id, }; - // prepare the sparse merkle tree nullifier proofs - for bundle in &bundles { - let mut partials = Vec::new(); - - for ptx in &bundle.ptxs { - let mut nf_proofs = Vec::new(); - - for input in &ptx.public.ptx.inputs { - let nf_proof = ledger.add_nullifier(input.nullifier); - nf_proofs.push(nf_proof); - } - - partials.push(LedgerPtxWitness { - ptx: ptx.public.clone(), - nf_proofs, - }); - } - - witness.bundles.push(LedgerBundleWitness { partials }) - } - let mut env = risc0_zkvm::ExecutorEnv::builder(); - for bundle in bundles { - for proof in bundle.proofs() { - env.add_assumption(proof); + // prepare the sparse merkle tree nullifier proofs + for proved_bundle in &bundles { + env.add_assumption(proved_bundle.risc0_receipt.clone()); + + let bundle = proved_bundle.public(); + println!("OUTSIDE LEDGER_PROOF {:?}", bundle); + println!("BUNDLE_ID {:?}", nomos_cl_bundle_risc0_proof::BUNDLE_ID); + + let zone_ledger_update = bundle + .zone_ledger_updates + .get(&zone_id) + .expect("why are we proving this bundle for this zone if it's not involved?"); + + let cm_root_proofs = + BTreeMap::from_iter(zone_ledger_update.cm_roots.iter().map(|root| { + // We make the simplifying assumption that bundle proofs + // are done w.r.t. the latest MMR (hence, empty merkle proofs) + // + // We can remove this assumption by tracking old MMR roots in the LedgerState + (*root, vec![]) + })); + + let mut nf_proofs = Vec::new(); + for nf in &zone_ledger_update.nullifiers { + let nf_proof = ledger.add_nullifier(*nf); + nf_proofs.push(nf_proof); } + + for cm in &zone_ledger_update.commitments { + ledger.add_commitment(cm); + } + + let ledger_bundle = LedgerBundleWitness { + bundle, + cm_root_proofs, + nf_proofs, + }; + + witness.bundles.push(ledger_bundle) } - for covenant in constraints { - env.add_assumption(covenant.risc0_receipt); - } + let env = env.write(&witness).unwrap().build().unwrap(); // Obtain the default prover. @@ -87,10 +73,7 @@ impl ProvedLedgerTransition { let opts = risc0_zkvm::ProverOpts::succinct(); let prove_info = prover .prove_with_opts(env, ledger_validity_proof::LEDGER_ELF, &opts) - .map_err(|e| { - eprintln!("{e}"); - Error::Risc0ProofFailed - })?; + .unwrap(); println!( "STARK 'ledger' prover time: {:.2?}, total_cycles: {}", @@ -98,14 +81,16 @@ impl ProvedLedgerTransition { prove_info.stats.total_cycles ); - Ok(Self { - public: prove_info - .receipt - .journal - .decode::() - .unwrap(), + Self { risc0_receipt: prove_info.receipt, - }) + } + } + + pub fn public(&self) -> LedgerProofPublic { + self.risc0_receipt + .journal + .decode::() + .unwrap() } pub fn verify(&self) -> bool { diff --git a/emmarin/cl/ledger/src/lib.rs b/emmarin/cl/ledger/src/lib.rs index d42f46c..ad327b3 100644 --- a/emmarin/cl/ledger/src/lib.rs +++ b/emmarin/cl/ledger/src/lib.rs @@ -1,4 +1,4 @@ -pub mod balance; +pub mod bundle; pub mod constraint; pub mod error; pub mod ledger; diff --git a/emmarin/cl/ledger/src/partial_tx.rs b/emmarin/cl/ledger/src/partial_tx.rs index 2d79956..4f73d95 100644 --- a/emmarin/cl/ledger/src/partial_tx.rs +++ b/emmarin/cl/ledger/src/partial_tx.rs @@ -11,7 +11,6 @@ use cl::cl::{ #[derive(Debug, Clone)] pub struct ProvedPartialTx { - pub public: PtxPublic, pub risc0_receipt: risc0_zkvm::Receipt, } @@ -42,7 +41,7 @@ impl ProvedPartialTx { // 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, nomos_cl_risc0_proofs::PTX_ELF, &opts) + .prove_with_opts(env, nomos_cl_ptx_risc0_proof::PTX_ELF, &opts) .map_err(|_| Error::Risc0ProofFailed)?; println!( @@ -52,14 +51,17 @@ impl ProvedPartialTx { ); Ok(Self { - public: prove_info.receipt.journal.decode()?, risc0_receipt: prove_info.receipt, }) } + pub fn public(&self) -> PtxPublic { + self.risc0_receipt.journal.decode().unwrap() + } + pub fn verify(&self) -> bool { self.risc0_receipt - .verify(nomos_cl_risc0_proofs::PTX_ID) + .verify(nomos_cl_ptx_risc0_proof::PTX_ID) .is_ok() } } diff --git a/emmarin/cl/ledger/src/zone_update.rs b/emmarin/cl/ledger/src/zone_update.rs index 988945b..1ffff33 100644 --- a/emmarin/cl/ledger/src/zone_update.rs +++ b/emmarin/cl/ledger/src/zone_update.rs @@ -18,12 +18,12 @@ impl ProvedUpdateBundle { return false; } - for bundle in &proof.public.cross_bundles { + for bundle in &proof.public().cross_bundles { expected_zones.insert(bundle.id, HashSet::from_iter(bundle.zones.clone())); actual_zones .entry(bundle.id) .or_insert_with(HashSet::new) - .insert(proof.public.id); + .insert(proof.public().id); } } @@ -48,8 +48,8 @@ impl ProvedUpdateBundle { return false; } - if ledger_proof.public.old_ledger != update.old.ledger - || ledger_proof.public.ledger != update.new.ledger + if ledger_proof.public().old_ledger != update.old.ledger + || ledger_proof.public().ledger != update.new.ledger { return false; } diff --git a/emmarin/cl/ledger/tests/simple_transfer.rs b/emmarin/cl/ledger/tests/simple_transfer.rs index e8700b6..b451b22 100644 --- a/emmarin/cl/ledger/tests/simple_transfer.rs +++ b/emmarin/cl/ledger/tests/simple_transfer.rs @@ -13,14 +13,10 @@ use cl::{ }, }; use ledger::{ - balance::ProvedBalance, - constraint::ConstraintProof, - ledger::{ProvedBundle, ProvedLedgerTransition}, - partial_tx::ProvedPartialTx, - stf::StfProof, - zone_update::ProvedUpdateBundle, + bundle::ProvedBundle, constraint::ConstraintProof, ledger::ProvedLedgerTransition, + partial_tx::ProvedPartialTx, stf::StfProof, zone_update::ProvedUpdateBundle, }; -use ledger_proof_statements::{balance::BalancePrivate, stf::StfPublic}; +use ledger_proof_statements::{bundle::BundlePrivate, stf::StfPublic}; use rand_core::CryptoRngCore; use std::sync::OnceLock; @@ -89,38 +85,32 @@ fn cross_transfer_transition( ) .unwrap(); - let balance = ProvedBalance::prove(&BalancePrivate { - balances: vec![ptx_witness.balance()], - }) - .unwrap(); + let bundle = ProvedBundle::prove( + &BundlePrivate { + bundle: vec![proved_ptx.public()], + balances: vec![ptx_witness.balance()], + }, + vec![proved_ptx], + ); - let zone_tx = ProvedBundle { - ptxs: vec![proved_ptx.clone()], - balance, - }; + println!("proving ledger A transition"); + let ledger_a_transition = + ProvedLedgerTransition::prove(ledger_a.clone(), zone_a, vec![bundle.clone()]); - let ledger_a_transition = ProvedLedgerTransition::prove( - ledger_a.clone(), - zone_a, - vec![zone_tx.clone()], - vec![constraint_proof], - ) - .unwrap(); + println!("proving ledger B transition"); + let ledger_b_transition = ProvedLedgerTransition::prove(ledger_b.clone(), zone_b, vec![bundle]); - let ledger_b_transition = - ProvedLedgerTransition::prove(ledger_b.clone(), zone_b, vec![zone_tx], vec![]).unwrap(); - - ledger_a.add_commitment(change.commit_note()); + ledger_a.add_commitment(&change.commit_note()); ledger_a.add_nullifier(input.nullifier()); - ledger_b.add_commitment(transfer.commit_note()); + ledger_b.add_commitment(&transfer.commit_note()); assert_eq!( - ledger_a_transition.public.ledger, + ledger_a_transition.public().ledger, ledger_a.to_witness().commit() ); assert_eq!( - ledger_b_transition.public.ledger, + ledger_b_transition.public().ledger, ledger_b.to_witness().commit() ); @@ -149,7 +139,7 @@ fn zone_update_cross() { let alice_input = InputWitness::from_output(utxo, alice.sk()); let mut ledger_a = LedgerState::default(); - let alice_cm_path = ledger_a.add_commitment(utxo.commit_note()); + let alice_cm_path = ledger_a.add_commitment(&utxo.commit_note()); let alice_cm_proof = (ledger_a.commitments.clone(), alice_cm_path); let ledger_b = LedgerState::default(); @@ -179,12 +169,12 @@ fn zone_update_cross() { ); let zone_a_new = ZoneNote { - ledger: ledger_a_transition.public.ledger, + ledger: ledger_a_transition.public().ledger, ..zone_a_old }; let zone_b_new = ZoneNote { - ledger: ledger_b_transition.public.ledger, + ledger: ledger_b_transition.public().ledger, ..zone_b_old }; diff --git a/emmarin/cl/ledger_proof_statements/Cargo.toml b/emmarin/cl/ledger_proof_statements/Cargo.toml index 65ea695..50bd1ca 100644 --- a/emmarin/cl/ledger_proof_statements/Cargo.toml +++ b/emmarin/cl/ledger_proof_statements/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] cl = { path = "../cl" } serde = { version = "1.0", features = ["derive"] } +sha2 = "0.10" diff --git a/emmarin/cl/ledger_proof_statements/src/balance.rs b/emmarin/cl/ledger_proof_statements/src/balance.rs deleted file mode 100644 index 509b1b5..0000000 --- a/emmarin/cl/ledger_proof_statements/src/balance.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cl::cl::{Balance, BalanceWitness}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct BalancePublic { - pub balances: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct BalancePrivate { - pub balances: Vec, -} diff --git a/emmarin/cl/ledger_proof_statements/src/bundle.rs b/emmarin/cl/ledger_proof_statements/src/bundle.rs new file mode 100644 index 0000000..abefbb3 --- /dev/null +++ b/emmarin/cl/ledger_proof_statements/src/bundle.rs @@ -0,0 +1,48 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use cl::{ + cl::{BalanceWitness, NoteCommitment, Nullifier}, + zone_layer::notes::ZoneId, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; + +use crate::ptx::PtxPublic; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct BundleId(pub [u8; 32]); + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BundlePublic { + pub bundle_id: BundleId, + pub zone_ledger_updates: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct LedgerUpdate { + // inputs in this bundle used the following roots in their cm membership proof. + pub cm_roots: BTreeSet<[u8; 32]>, + // these are the nullifiers of inputs used in this bundle. + pub nullifiers: Vec, + // these are commitments to created notes in this bundle + pub commitments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct BundlePrivate { + pub bundle: Vec, + pub balances: Vec, +} + +impl BundlePrivate { + pub fn id(&self) -> BundleId { + // TODO: change to merkle root + let mut hasher = Sha256::new(); + hasher.update(b"NOMOS_CL_BUNDLE_ID"); + for ptx in &self.bundle { + hasher.update(ptx.ptx.root().0); + } + + BundleId(hasher.finalize().into()) + } +} diff --git a/emmarin/cl/ledger_proof_statements/src/ledger.rs b/emmarin/cl/ledger_proof_statements/src/ledger.rs index 0355703..4766c89 100644 --- a/emmarin/cl/ledger_proof_statements/src/ledger.rs +++ b/emmarin/cl/ledger_proof_statements/src/ledger.rs @@ -1,6 +1,8 @@ -use crate::ptx::PtxPublic; -use cl::cl::merkle; -use cl::cl::{bundle::BundleId, Output}; +use std::collections::BTreeMap; + +use crate::bundle::BundleId; +use crate::bundle::BundlePublic; +use cl::cl::{merkle, NoteCommitment}; use cl::zone_layer::{ ledger::{Ledger, LedgerWitness}, notes::ZoneId, @@ -13,7 +15,7 @@ pub struct LedgerProofPublic { pub ledger: Ledger, pub id: ZoneId, pub cross_bundles: Vec, - pub outputs: Vec, + pub outputs: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -25,12 +27,8 @@ pub struct LedgerProofPrivate { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct LedgerBundleWitness { - pub partials: Vec, -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct LedgerPtxWitness { - pub ptx: PtxPublic, + pub bundle: BundlePublic, + pub cm_root_proofs: BTreeMap<[u8; 32], merkle::Path>, pub nf_proofs: Vec, } diff --git a/emmarin/cl/ledger_proof_statements/src/lib.rs b/emmarin/cl/ledger_proof_statements/src/lib.rs index 2000d4b..5d907dc 100644 --- a/emmarin/cl/ledger_proof_statements/src/lib.rs +++ b/emmarin/cl/ledger_proof_statements/src/lib.rs @@ -1,4 +1,4 @@ -pub mod balance; +pub mod bundle; pub mod constraint; pub mod ledger; pub mod ptx; diff --git a/emmarin/cl/ledger_proof_statements/src/ptx.rs b/emmarin/cl/ledger_proof_statements/src/ptx.rs index 425675f..c058c35 100644 --- a/emmarin/cl/ledger_proof_statements/src/ptx.rs +++ b/emmarin/cl/ledger_proof_statements/src/ptx.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PtxPublic { pub ptx: PartialTx, - pub cm_mmr: Vec, + pub cm_mmrs: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/emmarin/cl/ledger_validity_proof/ledger/Cargo.toml b/emmarin/cl/ledger_validity_proof/ledger/Cargo.toml index a05b1fa..8b6ec52 100644 --- a/emmarin/cl/ledger_validity_proof/ledger/Cargo.toml +++ b/emmarin/cl/ledger_validity_proof/ledger/Cargo.toml @@ -10,7 +10,7 @@ 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" } -nomos_cl_risc0_proofs = { path = "../../risc0_proofs" } +nomos_cl_bundle_risc0_proof = { path = "../../bundle_risc0_proof" } [patch.crates-io] # add RISC Zero accelerator support for all downstream usages of the following crates. diff --git a/emmarin/cl/ledger_validity_proof/ledger/src/main.rs b/emmarin/cl/ledger_validity_proof/ledger/src/main.rs index d803906..4c3d7b2 100644 --- a/emmarin/cl/ledger_validity_proof/ledger/src/main.rs +++ b/emmarin/cl/ledger_validity_proof/ledger/src/main.rs @@ -1,10 +1,6 @@ -use cl::{ - cl::{Bundle, Output}, - zone_layer::{ledger::LedgerWitness, notes::ZoneId}, -}; +use cl::cl::merkle; use ledger_proof_statements::{ - balance::BalancePublic, - ledger::{CrossZoneBundle, LedgerProofPrivate, LedgerProofPublic, LedgerPtxWitness}, + ledger::{CrossZoneBundle, LedgerProofPrivate, LedgerProofPublic, LedgerBundleWitness}, }; use risc0_zkvm::{guest::env, serde}; @@ -15,82 +11,44 @@ fn main() { bundles, } = env::read(); - let old_ledger = ledger.commit(); + let old_ledger = ledger.clone(); let mut cross_bundles = vec![]; let mut outputs = vec![]; - for bundle in bundles { - let balance_public = BalancePublic { - balances: bundle.partials.iter().map(|bundle_ptx| bundle_ptx.ptx.ptx.balance).collect::>(), - }; + for LedgerBundleWitness { bundle, cm_root_proofs, nf_proofs } in bundles { + println!("IN LEDGER PROOF {:?}", bundle); + println!("BUNDLE_ID {:?}", nomos_cl_bundle_risc0_proof::BUNDLE_ID); + env::verify(nomos_cl_bundle_risc0_proof::BUNDLE_ID, &serde::to_vec(&bundle).unwrap()).unwrap(); - // verify bundle is balanced - env::verify( - nomos_cl_risc0_proofs::BALANCE_ID, - &serde::to_vec(&balance_public).unwrap(), - ) - .unwrap(); + if let Some(ledger_update) = bundle.zone_ledger_updates.get(&id) { + for past_cm_root in &ledger_update.cm_roots { + let past_cm_root_proof = cm_root_proofs.get(past_cm_root).expect("missing cm root proof"); + let expected_current_cm_root = merkle::path_root(*past_cm_root, past_cm_root_proof); + assert!(old_ledger.valid_cm_root(expected_current_cm_root)) + } - for ptx in &bundle.partials { - let ptx_outputs = process_ptx(&mut ledger, ptx, id); - outputs.extend(ptx_outputs); + assert_eq!(ledger_update.nullifiers.len(), nf_proofs.len()); + for (nf, nf_proof) in ledger_update.nullifiers.iter().zip(nf_proofs) { + ledger.assert_nf_update(nf, &nf_proof); + } + + for cm in &ledger_update.commitments { + ledger.add_commitment(cm); + outputs.push(*cm) + } } - let bundle = Bundle { - partials: bundle.partials.into_iter().map(|ptx_witness| ptx_witness.ptx.ptx).collect(), - }; - let zones = bundle.zones(); - if zones.len() > 1 { - cross_bundles.push(CrossZoneBundle { - id: bundle.id(), - zones: zones.into_iter().collect(), - }); - } + cross_bundles.push(CrossZoneBundle { + id: bundle.bundle_id, + zones: bundle.zone_ledger_updates.into_keys().collect(), + }); } env::commit(&LedgerProofPublic { - old_ledger, + old_ledger: old_ledger.commit(), ledger: ledger.commit(), id, cross_bundles, outputs, }); } - -fn process_ptx( - ledger: &mut LedgerWitness, - ptx_witness: &LedgerPtxWitness, - zone_id: ZoneId, -) -> Vec { - let ptx = &ptx_witness.ptx; - let nf_proofs = &ptx_witness.nf_proofs; - - // always verify the ptx to ensure outputs were derived with the correct zone id - env::verify(nomos_cl_risc0_proofs::PTX_ID, &serde::to_vec(&ptx).unwrap()).unwrap(); - - - assert_eq!(ptx.ptx.inputs.len(), nf_proofs.len()); - assert_eq!(ptx.ptx.inputs.len(), ptx.cm_mmr.len()); - - for ((input, nf_proof), cm_mmr) in ptx.ptx.inputs.iter().zip(nf_proofs).zip(ptx.cm_mmr.iter()) { - if input.zone_id != zone_id { - continue; - } - - assert_eq!(cm_mmr, &ledger.commitments); // we force commitment proofs w.r.t. latest MMR - - ledger.assert_nf_update(input.nullifier, nf_proof); - } - - let mut outputs = vec![]; - for output in &ptx.ptx.outputs { - if output.zone_id != zone_id { - continue; - } - - ledger.commitments.push(&output.note_comm.0); - outputs.push(*output); - } - - outputs -} diff --git a/emmarin/cl/ptx_risc0_proof/Cargo.toml b/emmarin/cl/ptx_risc0_proof/Cargo.toml new file mode 100644 index 0000000..96515d9 --- /dev/null +++ b/emmarin/cl/ptx_risc0_proof/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nomos_cl_ptx_risc0_proof" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +risc0-build = { version = "1.0" } + +[package.metadata.risc0] +methods = ["ptx"] + diff --git a/emmarin/cl/ptx_risc0_proof/build.rs b/emmarin/cl/ptx_risc0_proof/build.rs new file mode 100644 index 0000000..08a8a4e --- /dev/null +++ b/emmarin/cl/ptx_risc0_proof/build.rs @@ -0,0 +1,3 @@ +fn main() { + risc0_build::embed_methods(); +} diff --git a/emmarin/cl/risc0_proofs/ptx/Cargo.toml b/emmarin/cl/ptx_risc0_proof/ptx/Cargo.toml similarity index 100% rename from emmarin/cl/risc0_proofs/ptx/Cargo.toml rename to emmarin/cl/ptx_risc0_proof/ptx/Cargo.toml diff --git a/emmarin/cl/risc0_proofs/ptx/src/main.rs b/emmarin/cl/ptx_risc0_proof/ptx/src/main.rs similarity index 72% rename from emmarin/cl/risc0_proofs/ptx/src/main.rs rename to emmarin/cl/ptx_risc0_proof/ptx/src/main.rs index 3115eab..bbf862c 100644 --- a/emmarin/cl/risc0_proofs/ptx/src/main.rs +++ b/emmarin/cl/ptx_risc0_proof/ptx/src/main.rs @@ -1,6 +1,9 @@ /// Input Proof -use ledger_proof_statements::{constraint::ConstraintPublic, ptx::{PtxPrivate, PtxPublic}}; -use risc0_zkvm::{serde, guest::env}; +use ledger_proof_statements::{ + constraint::ConstraintPublic, + ptx::{PtxPrivate, PtxPublic}, +}; +use risc0_zkvm::{guest::env, serde}; fn main() { let PtxPrivate { @@ -12,20 +15,21 @@ fn main() { let ptx_root = ptx_commit.root(); assert_eq!(ptx.inputs.len(), input_cm_proofs.len()); - let mut cm_mmr = Vec::new(); + let mut cm_mmrs = Vec::new(); for (input, (mmr, mmr_proof)) in ptx.inputs.iter().zip(input_cm_proofs) { let note_cm = input.note_commitment(); assert!(mmr.verify_proof(¬e_cm.0, &mmr_proof)); - cm_mmr.push(mmr); + cm_mmrs.push(mmr); env::verify( input.note.constraint.0, &serde::to_vec(&ConstraintPublic { ptx_root, nf: input.nullifier(), - }).unwrap(), - ).unwrap(); - + }) + .unwrap(), + ) + .unwrap(); } for output in ptx.outputs.iter() { @@ -34,6 +38,6 @@ fn main() { env::commit(&PtxPublic { ptx: ptx_commit, - cm_mmr, + cm_mmrs, }); } diff --git a/emmarin/cl/ptx_risc0_proof/src/lib.rs b/emmarin/cl/ptx_risc0_proof/src/lib.rs new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/emmarin/cl/ptx_risc0_proof/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/methods.rs")); diff --git a/emmarin/cl/risc0_proofs/Cargo.toml b/emmarin/cl/risc0_proofs/Cargo.toml index 19d562a..0c5b64d 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 = ["balance", "constraint_nop", "ptx", "stf_nop"] +methods = ["constraint_nop", "stf_nop"] diff --git a/emmarin/cl/risc0_proofs/balance/src/main.rs b/emmarin/cl/risc0_proofs/balance/src/main.rs deleted file mode 100644 index 3342001..0000000 --- a/emmarin/cl/risc0_proofs/balance/src/main.rs +++ /dev/null @@ -1,24 +0,0 @@ -use cl::cl::BalanceWitness; -/// Bundle Proof -/// -/// The bundle proof demonstrates that the set of partial transactions -/// balance to zero. i.e. \sum inputs = \sum outputs. -/// -/// This is done by proving knowledge of some blinding factor `r` s.t. -/// \sum outputs - \sum input = 0*G + r*H -/// -/// To avoid doing costly ECC in stark, we compute only the RHS in stark. -/// The sums and equality is checked outside of stark during proof verification. -use risc0_zkvm::guest::env; - -fn main() { - let balance_private: ledger_proof_statements::balance::BalancePrivate = env::read(); - - let balance_public = ledger_proof_statements::balance::BalancePublic { - balances: Vec::from_iter(balance_private.balances.iter().map(|b| b.commit())), - }; - - assert!(BalanceWitness::combine(balance_private.balances, [0u8; 16]).is_zero()); - - env::commit(&balance_public); -}