diff --git a/goas/atomic_asset_transfer/executor/src/lib.rs b/goas/atomic_asset_transfer/executor/src/lib.rs index e129eb7..5ac5ced 100644 --- a/goas/atomic_asset_transfer/executor/src/lib.rs +++ b/goas/atomic_asset_transfer/executor/src/lib.rs @@ -217,7 +217,10 @@ pub fn prove_user_atomic_transfer(atomic_transfer: UserAtomicTransfer) -> ledger #[cfg(test)] mod tests { - use cl::{note::unit_point, NoteWitness, NullifierNonce, OutputWitness, PartialTxWitness}; + use cl::{ + note::unit_point, BalanceWitness, NoteWitness, NullifierNonce, OutputWitness, + PartialTxWitness, + }; use common::{BoundTx, Deposit, Withdraw}; use goas_proof_statements::user_note::UserIntent; use ledger_proof_statements::death_constraint::DeathConstraintPublic; @@ -270,6 +273,7 @@ mod tests { zone_start.fund_input_witness(), ], outputs: vec![zone_end.state_note, zone_end.fund_note], + balance_blinding: BalanceWitness::random(&mut rng), }; let txs = vec![ @@ -293,12 +297,13 @@ mod tests { #[test] fn test_prove_zone_fund_constraint() { - let zone = - ZoneNotes::new_with_balances("ZONE", BTreeMap::from_iter([]), &mut rand::thread_rng()); + let mut rng = rand::thread_rng(); + let zone = ZoneNotes::new_with_balances("ZONE", BTreeMap::from_iter([]), &mut rng); let ptx = PartialTxWitness { inputs: vec![zone.fund_input_witness()], outputs: vec![zone.state_note], + balance_blinding: BalanceWitness::random(&mut rng), }; let proof = @@ -344,6 +349,7 @@ mod tests { let ptx = PartialTxWitness { inputs: vec![user_note], outputs: vec![zone_a.state_note, zone_b.state_note], + balance_blinding: BalanceWitness::random(&mut rng), }; let user_atomic_transfer = UserAtomicTransfer { diff --git a/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs b/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs index 975831d..afbeaba 100644 --- a/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs +++ b/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use cl::{BundleWitness, NoteWitness, NullifierNonce}; +use cl::{BalanceWitness, BundleWitness, NoteWitness, NullifierNonce}; use common::{new_account, BoundTx, Deposit, SignedBoundTx, Tx, Withdraw}; use executor::ZoneNotes; use goas_proof_statements::user_note::{UserAtomicTransfer, UserIntent}; @@ -43,6 +43,7 @@ fn test_atomic_transfer() { let user_ptx = cl::PartialTxWitness { inputs: vec![], outputs: vec![alice_intent_out], + balance_blinding: BalanceWitness::random(&mut rng), }; let zone_a_end = zone_a_start @@ -68,6 +69,7 @@ fn test_atomic_transfer() { zone_b_end.state_note, zone_b_end.fund_note, ], + balance_blinding: BalanceWitness::random(&mut rng), }; let signed_withdraw = SignedBoundTx::sign( @@ -165,7 +167,7 @@ fn test_atomic_transfer() { let bundle_witness = BundleWitness { balance_blinding: cl::BalanceWitness( - user_ptx.balance_blinding().0 + atomic_transfer_ptx.balance_blinding().0, + user_ptx.balance_blinding.0 + atomic_transfer_ptx.balance_blinding.0, ), }; diff --git a/goas/atomic_asset_transfer/executor/tests/deposit_ptx.rs b/goas/atomic_asset_transfer/executor/tests/deposit_ptx.rs index 1483fba..06587e3 100644 --- a/goas/atomic_asset_transfer/executor/tests/deposit_ptx.rs +++ b/goas/atomic_asset_transfer/executor/tests/deposit_ptx.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use cl::{NoteWitness, NullifierSecret}; +use cl::{BalanceWitness, NoteWitness, NullifierSecret}; use common::{new_account, BoundTx, SignedBoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT}; use executor::ZoneNotes; use ledger::death_constraint::DeathProof; @@ -22,7 +22,7 @@ fn test_deposit() { let zone_end = zone_start.clone().run([Tx::Deposit(deposit)]); - let alice_deposit = cl::InputWitness::random( + let alice_deposit = cl::InputWitness::from_output( cl::OutputWitness::random( NoteWitness::stateless( 78, @@ -33,12 +33,12 @@ fn test_deposit() { &mut rng, ), alice_cl_sk, - &mut rng, ); let deposit_ptx = cl::PartialTxWitness { inputs: vec![zone_start.state_input_witness(), alice_deposit], outputs: vec![zone_end.state_note, zone_end.fund_note], + balance_blinding: BalanceWitness::random(&mut rng), }; let signed_deposit = SignedBoundTx::sign( @@ -77,10 +77,7 @@ fn test_deposit() { assert!(deposit_proof.verify()); - assert_eq!( - deposit_proof.outputs[0].output, - zone_end.state_note.commit() - ); + assert_eq!(deposit_proof.ptx.outputs[0], zone_end.state_note.commit()); assert_eq!( zone_end.state_note.note.state, StateWitness { @@ -92,7 +89,7 @@ fn test_deposit() { .0 ); assert_eq!( - deposit_ptx.commit().balance(), - cl::Balance::zero(deposit_ptx.balance_blinding()) + deposit_ptx.commit().balance, + cl::Balance::zero(deposit_ptx.balance_blinding) ); } diff --git a/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs b/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs index 5f099d1..5d1860a 100644 --- a/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs +++ b/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use cl::{NoteWitness, NullifierSecret}; +use cl::{BalanceWitness, NoteWitness, NullifierSecret}; use common::{new_account, BoundTx, SignedBoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT}; use executor::ZoneNotes; use ledger::death_constraint::DeathProof; @@ -16,14 +16,13 @@ fn test_withdrawal() { let zone_start = ZoneNotes::new_with_balances("ZONE", BTreeMap::from_iter([(alice_vk, 100)]), &mut rng); - let alice_intent = cl::InputWitness::random( + let alice_intent = cl::InputWitness::from_output( cl::OutputWitness::random( NoteWitness::stateless(1, *ZONE_CL_FUNDS_UNIT, DeathProof::nop_constraint()), // TODO, intent should be in the death constraint alice_cl_sk.commit(), &mut rng, ), alice_cl_sk, - &mut rng, ); let withdraw = common::Withdraw { @@ -50,6 +49,7 @@ fn test_withdrawal() { alice_intent, ], outputs: vec![zone_end.state_note, zone_end.fund_note, alice_withdrawal], + balance_blinding: BalanceWitness::random(&mut rng), }; let signed_withdraw = SignedBoundTx::sign( @@ -97,10 +97,7 @@ fn test_withdrawal() { assert!(withdraw_proof.verify()); - assert_eq!( - withdraw_proof.outputs[0].output, - zone_end.state_note.commit() - ); + assert_eq!(withdraw_proof.ptx.outputs[0], zone_end.state_note.commit()); assert_eq!( zone_end.state_note.note.state, StateWitness { diff --git a/goas/atomic_asset_transfer/risc0_proofs/zone_state/src/main.rs b/goas/atomic_asset_transfer/risc0_proofs/zone_state/src/main.rs index ff18581..b205142 100644 --- a/goas/atomic_asset_transfer/risc0_proofs/zone_state/src/main.rs +++ b/goas/atomic_asset_transfer/risc0_proofs/zone_state/src/main.rs @@ -32,12 +32,6 @@ fn validate_zone_transition( // nullifier secret is propagated assert_eq!(in_note.input.nf_sk.commit(), out_note.output.nf_pk); - // balance blinding is propagated - assert_eq!( - in_note.input.balance_blinding, - out_note.output.balance_blinding - ); - // the nonce is correctly evolved assert_eq!(in_note.input.evolved_nonce(b"STATE_NONCE"), out_note.output.nonce); diff --git a/goas/cl/cl/src/balance.rs b/goas/cl/cl/src/balance.rs index d7cd261..a453593 100644 --- a/goas/cl/cl/src/balance.rs +++ b/goas/cl/cl/src/balance.rs @@ -1,4 +1,7 @@ -use curve25519_dalek::{ristretto::RistrettoPoint, Scalar}; +use curve25519_dalek::{ + constants::RISTRETTO_BASEPOINT_POINT, ristretto::RistrettoPoint, traits::VartimeMultiscalarMul, + Scalar, +}; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; @@ -42,8 +45,33 @@ impl BalanceWitness { Self::new(Scalar::random(&mut rng)) } - pub fn commit(&self, note: &NoteWitness) -> Balance { - Balance(balance(note.value, note.unit, self.0)) + pub fn commit<'a>( + &self, + inputs: impl IntoIterator, + outputs: impl IntoIterator, + ) -> Balance { + let (input_points, input_scalars): (Vec<_>, Vec<_>) = inputs + .into_iter() + .map(|i| (i.unit, -Scalar::from(i.value))) + .unzip(); + + let (output_points, output_scalars): (Vec<_>, Vec<_>) = outputs + .into_iter() + .map(|o| (o.unit, Scalar::from(o.value))) + .unzip(); + + let points = input_points + .into_iter() + .chain(output_points) + .chain([RISTRETTO_BASEPOINT_POINT]); + let scalars = input_scalars + .into_iter() + .chain(output_scalars) + .chain([self.0]); + + let blinded_balance = RistrettoPoint::vartime_multiscalar_mul(scalars, points); + + Balance(blinded_balance) } } @@ -66,8 +94,8 @@ mod test { let mut rng = rand::thread_rng(); let b = BalanceWitness::random(&mut rng); assert_eq!( - b.commit(&NoteWitness::basic(0, nmo)), - b.commit(&NoteWitness::basic(0, eth)), + b.commit([&NoteWitness::basic(0, nmo)], []), + b.commit([&NoteWitness::basic(0, eth)], []), ); } @@ -83,15 +111,15 @@ mod test { let note = NoteWitness::basic(10, nmo); - let a = bal_a.commit(¬e); - let b = bal_b.commit(¬e); + let a = bal_a.commit([¬e], []); + let b = bal_b.commit([¬e], []); assert_ne!(a, b); let diff_note = NoteWitness::basic(0, nmo); assert_eq!( a.0 - b.0, - BalanceWitness::new(r_a - r_b).commit(&diff_note).0 + BalanceWitness::new(r_a - r_b).commit([&diff_note], []).0 ); } @@ -104,7 +132,7 @@ mod test { let nmo = NoteWitness::basic(10, nmo); let eth = NoteWitness::basic(10, eth); - assert_ne!(b.commit(&nmo), b.commit(ð)); + assert_ne!(b.commit([&nmo], []), b.commit([ð], [])); } #[test] @@ -123,14 +151,14 @@ mod test { // Values of same unit are homomorphic assert_eq!( - (b1.commit(&ten).0 - b1.commit(&eight).0), - b_zero.commit(&two).0 + (b1.commit([&ten], []).0 - b1.commit([&eight], []).0), + b_zero.commit([&two], []).0 ); // Blinding factors are also homomorphic. assert_eq!( - b1.commit(&ten).0 - b2.commit(&ten).0, - BalanceWitness::new(b1.0 - b2.0).commit(&zero).0 + b1.commit([&ten], []).0 - b2.commit([&ten], []).0, + BalanceWitness::new(b1.0 - b2.0).commit([&zero], []).0 ); } } diff --git a/goas/cl/cl/src/bundle.rs b/goas/cl/cl/src/bundle.rs index db9f238..fe5f07c 100644 --- a/goas/cl/cl/src/bundle.rs +++ b/goas/cl/cl/src/bundle.rs @@ -18,7 +18,7 @@ pub struct BundleWitness { impl Bundle { pub fn balance(&self) -> Balance { - Balance(self.partials.iter().map(|ptx| ptx.balance().0).sum()) + Balance(self.partials.iter().map(|ptx| ptx.balance.0).sum()) } pub fn is_balanced(&self, witness: BalanceWitness) -> bool { @@ -28,6 +28,8 @@ impl Bundle { #[cfg(test)] mod test { + use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, Scalar}; + use crate::{ input::InputWitness, note::{unit_point, NoteWitness}, @@ -49,11 +51,11 @@ mod test { let nmo_10_utxo = OutputWitness::random(NoteWitness::basic(10, nmo), nf_a.commit(), &mut rng); - let nmo_10_in = InputWitness::random(nmo_10_utxo, nf_a, &mut rng); + let nmo_10_in = InputWitness::from_output(nmo_10_utxo, nf_a); let eth_23_utxo = OutputWitness::random(NoteWitness::basic(23, eth), nf_b.commit(), &mut rng); - let eth_23_in = InputWitness::random(eth_23_utxo, nf_b, &mut rng); + let eth_23_in = InputWitness::from_output(eth_23_utxo, nf_b); let crv_4840_out = OutputWitness::random(NoteWitness::basic(4840, crv), nf_c.commit(), &mut rng); @@ -61,28 +63,28 @@ mod test { let ptx_unbalanced = PartialTxWitness { inputs: vec![nmo_10_in, eth_23_in], outputs: vec![crv_4840_out], + balance_blinding: BalanceWitness::random(&mut rng), }; let bundle_witness = BundleWitness { - balance_blinding: BalanceWitness::new( - crv_4840_out.balance_blinding.0 - - nmo_10_in.balance_blinding.0 - - eth_23_in.balance_blinding.0, - ), + balance_blinding: ptx_unbalanced.balance_blinding, }; let mut bundle = Bundle { partials: vec![ptx_unbalanced.commit()], }; + let crv_4840_out_bal = crv_4840_out.note.unit * Scalar::from(crv_4840_out.note.value); + let nmo_10_in_bal = nmo_10_in.note.unit * Scalar::from(nmo_10_in.note.value); + let eth_23_in_bal = eth_23_in.note.unit * Scalar::from(eth_23_in.note.value); + let unbalance_blinding = RISTRETTO_BASEPOINT_POINT * ptx_unbalanced.balance_blinding.0; assert!(!bundle.is_balanced(bundle_witness.balance_blinding)); assert_eq!( bundle.balance().0, - crv_4840_out.commit().balance.0 - - (nmo_10_in.commit().balance.0 + eth_23_in.commit().balance.0) + crv_4840_out_bal - (nmo_10_in_bal + eth_23_in_bal) + unbalance_blinding ); - let crv_4840_in = InputWitness::random(crv_4840_out, nf_c, &mut rng); + let crv_4840_in = InputWitness::from_output(crv_4840_out, nf_c); let nmo_10_out = OutputWitness::random( NoteWitness::basic(10, nmo), NullifierSecret::random(&mut rng).commit(), // transferring to a random owner @@ -94,21 +96,17 @@ mod test { &mut rng, ); - bundle.partials.push( - PartialTxWitness { - inputs: vec![crv_4840_in], - outputs: vec![nmo_10_out, eth_23_out], - } - .commit(), - ); + let ptx_solved = PartialTxWitness { + inputs: vec![crv_4840_in], + outputs: vec![nmo_10_out, eth_23_out], + balance_blinding: BalanceWitness::random(&mut rng), + }; + + bundle.partials.push(ptx_solved.commit()); let witness = BundleWitness { balance_blinding: BalanceWitness::new( - -nmo_10_in.balance_blinding.0 - eth_23_in.balance_blinding.0 - + crv_4840_out.balance_blinding.0 - - crv_4840_in.balance_blinding.0 - + nmo_10_out.balance_blinding.0 - + eth_23_out.balance_blinding.0, + ptx_unbalanced.balance_blinding.0 + ptx_solved.balance_blinding.0, ), }; diff --git a/goas/cl/cl/src/input.rs b/goas/cl/cl/src/input.rs index 8fa32d2..404e648 100644 --- a/goas/cl/cl/src/input.rs +++ b/goas/cl/cl/src/input.rs @@ -3,39 +3,29 @@ /// Partial transactions, as the name suggests, are transactions /// which on their own may not balance (i.e. \sum inputs != \sum outputs) use crate::{ - balance::Balance, note::{DeathCommitment, NoteWitness}, nullifier::{Nullifier, NullifierNonce, NullifierSecret}, - BalanceWitness, }; -use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct Input { pub nullifier: Nullifier, - pub balance: Balance, pub death_cm: DeathCommitment, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct InputWitness { pub note: NoteWitness, - pub balance_blinding: BalanceWitness, pub nf_sk: NullifierSecret, pub nonce: NullifierNonce, } impl InputWitness { - pub fn random( - output: crate::OutputWitness, - nf_sk: NullifierSecret, - mut rng: impl CryptoRngCore, - ) -> Self { + pub fn from_output(output: crate::OutputWitness, nf_sk: NullifierSecret) -> Self { assert_eq!(nf_sk.commit(), output.nf_pk); Self { note: output.note, - balance_blinding: BalanceWitness::random(&mut rng), nf_sk, nonce: output.nonce, } @@ -46,7 +36,6 @@ impl InputWitness { assert_eq!(nf_sk.commit(), output.nf_pk); // ensure the output was a public UTXO Self { note: output.note, - balance_blinding: BalanceWitness::unblinded(), nf_sk, nonce: output.nonce, } @@ -56,14 +45,9 @@ impl InputWitness { self.nonce.evolve(domain, &self.nf_sk, &self.note) } - pub fn evolve_output( - &self, - domain: &[u8], - balance_blinding: BalanceWitness, - ) -> crate::OutputWitness { + pub fn evolve_output(&self, domain: &[u8]) -> crate::OutputWitness { crate::OutputWitness { note: self.note, - balance_blinding, nf_pk: self.nf_sk.commit(), nonce: self.evolved_nonce(domain), } @@ -76,7 +60,6 @@ impl InputWitness { pub fn commit(&self) -> Input { Input { nullifier: self.nullifier(), - balance: self.balance_blinding.commit(&self.note), death_cm: self.note.death_commitment(), } } @@ -87,11 +70,10 @@ impl InputWitness { } impl Input { - pub fn to_bytes(&self) -> [u8; 96] { - let mut bytes = [0u8; 96]; + pub fn to_bytes(&self) -> [u8; 64] { + let mut bytes = [0u8; 64]; bytes[..32].copy_from_slice(self.nullifier.as_bytes()); - bytes[32..64].copy_from_slice(&self.balance.to_bytes()); - bytes[64..96].copy_from_slice(&self.death_cm.0); + bytes[32..64].copy_from_slice(&self.death_cm.0); bytes } } diff --git a/goas/cl/cl/src/output.rs b/goas/cl/cl/src/output.rs index 0e611ec..6880440 100644 --- a/goas/cl/cl/src/output.rs +++ b/goas/cl/cl/src/output.rs @@ -2,23 +2,19 @@ use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; use crate::{ - balance::Balance, - error::Error, note::{NoteCommitment, NoteWitness}, nullifier::{NullifierCommitment, NullifierNonce}, - BalanceWitness, NullifierSecret, + NullifierSecret, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct Output { pub note_comm: NoteCommitment, - pub balance: Balance, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct OutputWitness { pub note: NoteWitness, - pub balance_blinding: BalanceWitness, pub nf_pk: NullifierCommitment, pub nonce: NullifierNonce, } @@ -31,7 +27,6 @@ impl OutputWitness { ) -> Self { Self { note, - balance_blinding: BalanceWitness::random(&mut rng), nf_pk: owner, nonce: NullifierNonce::random(&mut rng), } @@ -40,7 +35,6 @@ impl OutputWitness { pub fn public(note: NoteWitness, nonce: NullifierNonce) -> Self { Self { note, - balance_blinding: BalanceWitness::unblinded(), nf_pk: NullifierSecret::zero().commit(), nonce, } @@ -50,99 +44,15 @@ impl OutputWitness { self.note.commit(self.nf_pk, self.nonce) } - pub fn commit_balance(&self) -> Balance { - self.balance_blinding.commit(&self.note) - } - pub fn commit(&self) -> Output { Output { note_comm: self.commit_note(), - balance: self.commit_balance(), } } } -// as we don't have SNARKS hooked up yet, the witness will be our proof -#[derive(Debug, Clone)] -pub struct OutputProof(OutputWitness); - impl Output { - pub fn prove(&self, w: &OutputWitness) -> Result { - if &w.commit() == self { - Ok(OutputProof(*w)) - } else { - Err(Error::ProofFailed) - } - } - - pub fn verify(&self, proof: &OutputProof) -> bool { - // verification checks the relation - // - note_comm == commit(note || nf_pk) - // - balance == v * hash_to_curve(Unit) + blinding * H - let witness = &proof.0; - - self.note_comm == witness.commit_note() && self.balance == witness.commit_balance() - } - - pub fn to_bytes(&self) -> [u8; 64] { - let mut bytes = [0u8; 64]; - bytes[..32].copy_from_slice(self.note_comm.as_bytes()); - bytes[32..64].copy_from_slice(&self.balance.to_bytes()); - bytes - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{note::unit_point, nullifier::NullifierSecret}; - - #[test] - fn test_output_proof() { - let (nmo, eth) = (unit_point("NMO"), unit_point("ETH")); - let mut rng = rand::thread_rng(); - - let witness = OutputWitness { - note: NoteWitness::basic(10, nmo), - balance_blinding: BalanceWitness::random(&mut rng), - nf_pk: NullifierSecret::random(&mut rng).commit(), - nonce: NullifierNonce::random(&mut rng), - }; - - let output = witness.commit(); - let proof = output.prove(&witness).unwrap(); - - assert!(output.verify(&proof)); - - let wrong_witnesses = [ - OutputWitness { - note: NoteWitness::basic(11, nmo), - ..witness - }, - OutputWitness { - note: NoteWitness::basic(10, eth), - ..witness - }, - OutputWitness { - balance_blinding: BalanceWitness::random(&mut rng), - ..witness - }, - OutputWitness { - nf_pk: NullifierSecret::random(&mut rng).commit(), - ..witness - }, - OutputWitness { - nonce: NullifierNonce::random(&mut rng), - ..witness - }, - ]; - - for wrong_witness in wrong_witnesses { - assert!(output.prove(&wrong_witness).is_err()); - - let wrong_output = wrong_witness.commit(); - let wrong_proof = wrong_output.prove(&wrong_witness).unwrap(); - assert!(!output.verify(&wrong_proof)); - } + pub fn to_bytes(&self) -> [u8; 32] { + self.note_comm.0 } } diff --git a/goas/cl/cl/src/partial_tx.rs b/goas/cl/cl/src/partial_tx.rs index 4854cd9..eefc41d 100644 --- a/goas/cl/cl/src/partial_tx.rs +++ b/goas/cl/cl/src/partial_tx.rs @@ -1,6 +1,4 @@ -use curve25519_dalek::ristretto::RistrettoPoint; -use curve25519_dalek::Scalar; -use rand_core::RngCore; +use rand_core::{CryptoRngCore, RngCore}; use serde::{Deserialize, Serialize}; use crate::balance::{Balance, BalanceWitness}; @@ -34,33 +32,44 @@ impl PtxRoot { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PartialTx { pub inputs: Vec, pub outputs: Vec, + pub balance: Balance, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PartialTxWitness { pub inputs: Vec, pub outputs: Vec, + pub balance_blinding: BalanceWitness, } impl PartialTxWitness { + pub fn random( + inputs: Vec, + outputs: Vec, + mut rng: impl CryptoRngCore, + ) -> Self { + Self { + inputs, + outputs, + balance_blinding: BalanceWitness::random(&mut rng), + } + } + pub fn commit(&self) -> PartialTx { PartialTx { inputs: Vec::from_iter(self.inputs.iter().map(InputWitness::commit)), outputs: Vec::from_iter(self.outputs.iter().map(OutputWitness::commit)), + balance: self.balance_blinding.commit( + self.inputs.iter().map(|i| &i.note), + self.outputs.iter().map(|o| &o.note), + ), } } - pub fn balance_blinding(&self) -> BalanceWitness { - let in_sum: Scalar = self.inputs.iter().map(|i| i.balance_blinding.0).sum(); - let out_sum: Scalar = self.outputs.iter().map(|o| o.balance_blinding.0).sum(); - - BalanceWitness(out_sum - in_sum) - } - pub fn input_witness(&self, idx: usize) -> PartialTxInputWitness { let input_bytes = Vec::from_iter(self.inputs.iter().map(|i| i.commit().to_bytes().to_vec())); @@ -107,13 +116,6 @@ impl PartialTx { let root = merkle::node(input_root, output_root); PtxRoot(root) } - - pub fn balance(&self) -> Balance { - let in_sum: RistrettoPoint = self.inputs.iter().map(|i| i.balance.0).sum(); - let out_sum: RistrettoPoint = self.outputs.iter().map(|o| o.balance.0).sum(); - - Balance(out_sum - in_sum) - } } /// An input to a partial transaction @@ -147,6 +149,8 @@ impl PartialTxOutputWitness { #[cfg(test)] mod test { + use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, Scalar}; + use crate::{ note::{unit_point, NoteWitness}, nullifier::NullifierSecret, @@ -165,11 +169,11 @@ mod test { let nmo_10_utxo = OutputWitness::random(NoteWitness::basic(10, nmo), nf_a.commit(), &mut rng); - let nmo_10 = InputWitness::random(nmo_10_utxo, nf_a, &mut rng); + let nmo_10 = InputWitness::from_output(nmo_10_utxo, nf_a); let eth_23_utxo = OutputWitness::random(NoteWitness::basic(23, eth), nf_b.commit(), &mut rng); - let eth_23 = InputWitness::random(eth_23_utxo, nf_b, &mut rng); + let eth_23 = InputWitness::from_output(eth_23_utxo, nf_b); let crv_4840 = OutputWitness::random(NoteWitness::basic(4840, crv), nf_c.commit(), &mut rng); @@ -177,13 +181,18 @@ mod test { let ptx_witness = PartialTxWitness { inputs: vec![nmo_10, eth_23], outputs: vec![crv_4840], + balance_blinding: BalanceWitness::random(&mut rng), }; let ptx = ptx_witness.commit(); + let crv_4840_bal = crv_4840.note.unit * Scalar::from(crv_4840.note.value); + let nmo_10_bal = nmo_10.note.unit * Scalar::from(nmo_10.note.value); + let eth_23_bal = eth_23.note.unit * Scalar::from(eth_23.note.value); + let blinding = RISTRETTO_BASEPOINT_POINT * ptx_witness.balance_blinding.0; assert_eq!( - ptx.balance().0, - crv_4840.commit().balance.0 - (nmo_10.commit().balance.0 + eth_23.commit().balance.0) + ptx.balance.0, + crv_4840_bal - (nmo_10_bal + eth_23_bal) + blinding, ); } } diff --git a/goas/cl/cl/tests/simple_transfer.rs b/goas/cl/cl/tests/simple_transfer.rs index 69b15c4..a0c6245 100644 --- a/goas/cl/cl/tests/simple_transfer.rs +++ b/goas/cl/cl/tests/simple_transfer.rs @@ -1,4 +1,4 @@ -use cl::note::unit_point; +use cl::{note::unit_point, BalanceWitness}; use rand_core::CryptoRngCore; fn receive_utxo( @@ -29,8 +29,9 @@ fn test_simple_transfer() { cl::OutputWitness::random(cl::NoteWitness::basic(2, nmo), sender_nf_pk, &mut rng); let ptx_witness = cl::PartialTxWitness { - inputs: vec![cl::InputWitness::random(utxo, sender_nf_sk, &mut rng)], + inputs: vec![cl::InputWitness::from_output(utxo, sender_nf_sk)], outputs: vec![recipient_output, change_output], + balance_blinding: BalanceWitness::random(&mut rng), }; let ptx = ptx_witness.commit(); @@ -39,5 +40,5 @@ fn test_simple_transfer() { partials: vec![ptx], }; - assert!(bundle.is_balanced(ptx_witness.balance_blinding())) + assert!(bundle.is_balanced(ptx_witness.balance_blinding)) } diff --git a/goas/cl/ledger/src/input.rs b/goas/cl/ledger/src/input.rs deleted file mode 100644 index f1ea9ee..0000000 --- a/goas/cl/ledger/src/input.rs +++ /dev/null @@ -1,181 +0,0 @@ -use ledger_proof_statements::input::{InputPrivate, InputPublic}; - -use crate::error::{Error, Result}; - -const MAX_NOTE_COMMS: usize = 2usize.pow(8); - -#[derive(Debug, Clone)] -pub struct ProvedInput { - pub input: InputPublic, - pub risc0_receipt: risc0_zkvm::Receipt, -} - -impl ProvedInput { - pub fn prove( - input: &cl::InputWitness, - note_commitments: &[cl::NoteCommitment], - ) -> Result { - let output_cm = input.note_commitment(); - - let cm_leaves = note_commitment_leaves(note_commitments); - let cm_idx = note_commitments - .iter() - .position(|c| c == &output_cm) - .unwrap(); - let cm_path = cl::merkle::path(cm_leaves, cm_idx); - - let secrets = InputPrivate { - input: *input, - cm_path, - }; - - let env = risc0_zkvm::ExecutorEnv::builder() - .write(&secrets) - .unwrap() - .build() - .unwrap(); - - // Obtain the default prover. - let prover = risc0_zkvm::default_prover(); - - let start_t = std::time::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, nomos_cl_risc0_proofs::INPUT_ELF, &opts) - .map_err(|_| Error::Risc0ProofFailed)?; - - println!( - "STARK 'input' prover time: {:.2?}, total_cycles: {}", - start_t.elapsed(), - prove_info.stats.total_cycles - ); - // extract the receipt. - let receipt = prove_info.receipt; - - Ok(Self { - input: InputPublic { - cm_root: cl::merkle::root(cm_leaves), - input: input.commit(), - }, - risc0_receipt: receipt, - }) - } - - pub fn public(&self) -> Result { - Ok(self.risc0_receipt.journal.decode()?) - } - - pub fn verify(&self) -> bool { - let Ok(proved_public_inputs) = self.public() else { - return false; - }; - - self.input == proved_public_inputs - && self - .risc0_receipt - .verify(nomos_cl_risc0_proofs::INPUT_ID) - .is_ok() - } -} - -fn note_commitment_leaves(note_commitments: &[cl::NoteCommitment]) -> [[u8; 32]; MAX_NOTE_COMMS] { - let note_comm_bytes = Vec::from_iter(note_commitments.iter().map(|c| c.as_bytes().to_vec())); - let cm_leaves = cl::merkle::padded_leaves::(¬e_comm_bytes); - cm_leaves -} - -#[cfg(test)] -mod test { - use super::*; - use cl::note::unit_point; - use rand::thread_rng; - - #[test] - fn test_input_prover() { - let nmo = unit_point("NMO"); - - let mut rng = thread_rng(); - - let input = cl::InputWitness { - note: cl::NoteWitness::basic(32, nmo), - balance_blinding: cl::BalanceWitness::random(&mut rng), - nf_sk: cl::NullifierSecret::random(&mut rng), - nonce: cl::NullifierNonce::random(&mut rng), - }; - - let notes = vec![input.note_commitment()]; - - let mut proved_input = ProvedInput::prove(&input, ¬es).unwrap(); - - let expected_public_inputs = InputPublic { - cm_root: cl::merkle::root(note_commitment_leaves(¬es)), - input: input.commit(), - }; - - assert_eq!(proved_input.input, expected_public_inputs); - assert!(proved_input.verify()); - - let wrong_public_inputs = [ - InputPublic { - cm_root: cl::merkle::root([cl::merkle::leaf(b"bad_root")]), - ..expected_public_inputs - }, - InputPublic { - input: cl::Input { - nullifier: cl::Nullifier::new( - cl::NullifierSecret::random(&mut rng), - input.note_commitment(), - ), - ..expected_public_inputs.input - }, - ..expected_public_inputs - }, - InputPublic { - input: cl::Input { - death_cm: cl::note::death_commitment(b"wrong death vk"), - ..expected_public_inputs.input - }, - ..expected_public_inputs - }, - InputPublic { - input: cl::Input { - balance: cl::BalanceWitness::random(&mut rng) - .commit(&cl::NoteWitness::basic(32, nmo)), - ..expected_public_inputs.input - }, - ..expected_public_inputs - }, - ]; - - for wrong_input in wrong_public_inputs { - proved_input.input = wrong_input; - assert!(!proved_input.verify()); - } - } - - // ----- The following tests still need to be built. ----- - // #[test] - // fn test_input_ptx_coupling() { - // let mut rng = rand::thread_rng(); - - // let note = cl::NoteWitness::new(10, "NMO", [0u8; 32], &mut rng); - // let nf_sk = cl::NullifierSecret::random(&mut rng); - // let nonce = cl::NullifierNonce::random(&mut rng); - - // let witness = cl::InputWitness { note, nf_sk, nonce }; - - // let input = witness.commit(); - - // let ptx_root = cl::PtxRoot::random(&mut rng); - // let proof = input.prove(&witness, ptx_root, vec![]).unwrap(); - - // assert!(input.verify(ptx_root, &proof)); - - // // The same input proof can not be used in another partial transaction. - // let another_ptx_root = cl::PtxRoot::random(&mut rng); - // assert!(!input.verify(another_ptx_root, &proof)); - // } -} diff --git a/goas/cl/ledger/src/lib.rs b/goas/cl/ledger/src/lib.rs index d5b1744..e365360 100644 --- a/goas/cl/ledger/src/lib.rs +++ b/goas/cl/ledger/src/lib.rs @@ -1,8 +1,6 @@ pub mod bundle; pub mod death_constraint; pub mod error; -pub mod input; -pub mod output; pub mod partial_tx; pub use death_constraint::DeathProof; diff --git a/goas/cl/ledger/src/output.rs b/goas/cl/ledger/src/output.rs deleted file mode 100644 index 89a9238..0000000 --- a/goas/cl/ledger/src/output.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::error::{Error, Result}; - -pub struct ProvedOutput { - pub output: cl::Output, - pub risc0_receipt: risc0_zkvm::Receipt, -} - -impl ProvedOutput { - pub fn prove(witness: &cl::OutputWitness) -> Result { - let env = risc0_zkvm::ExecutorEnv::builder() - .write(&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::OUTPUT_ELF, &opts) - .map_err(|_| Error::Risc0ProofFailed)?; - - println!( - "STARK 'output' prover time: {:.2?}, total_cycles: {}", - start_t.elapsed(), - prove_info.stats.total_cycles - ); - - let receipt = prove_info.receipt; - - Ok(Self { - output: witness.commit(), - risc0_receipt: receipt, - }) - } - - pub fn public(&self) -> Result { - Ok(self.risc0_receipt.journal.decode()?) - } - - pub fn verify(&self) -> bool { - let Ok(output_commitments) = self.public() else { - return false; - }; - - self.output == output_commitments - && self - .risc0_receipt - .verify(nomos_cl_risc0_proofs::OUTPUT_ID) - .is_ok() - } -} - -#[cfg(test)] -mod test { - use super::*; - use cl::note::unit_point; - use rand::thread_rng; - - #[test] - fn test_output_prover() { - let nmo = unit_point("NMO"); - - let mut rng = thread_rng(); - - let output = cl::OutputWitness { - note: cl::NoteWitness::basic(32, nmo), - balance_blinding: cl::BalanceWitness::random(&mut rng), - nf_pk: cl::NullifierSecret::random(&mut rng).commit(), - nonce: cl::NullifierNonce::random(&mut rng), - }; - - let mut proved_output = ProvedOutput::prove(&output).unwrap(); - - let expected_output_cm = output.commit(); - - assert_eq!(proved_output.output, expected_output_cm); - assert!(proved_output.verify()); - - let wrong_output_cms = [ - cl::Output { - note_comm: cl::NoteWitness::basic(100, nmo).commit( - cl::NullifierSecret::random(&mut rng).commit(), - cl::NullifierNonce::random(&mut rng), - ), - ..expected_output_cm - }, - cl::Output { - note_comm: cl::NoteWitness::basic(100, nmo).commit( - cl::NullifierSecret::random(&mut rng).commit(), - cl::NullifierNonce::random(&mut rng), - ), - balance: cl::BalanceWitness::random(&mut rng) - .commit(&cl::NoteWitness::basic(100, nmo)), - }, - ]; - - for wrong_output_cm in wrong_output_cms { - proved_output.output = wrong_output_cm; - assert!(!proved_output.verify()); - } - } - - #[test] - fn test_zero_output_is_rejected() { - let nmo = unit_point("NMO"); - let mut rng = thread_rng(); - - let output = cl::OutputWitness::random( - cl::NoteWitness::basic(0, nmo), - cl::NullifierSecret::random(&mut rng).commit(), - &mut rng, - ); - - assert!(ProvedOutput::prove(&output).is_err()); - } -} diff --git a/goas/cl/ledger/src/partial_tx.rs b/goas/cl/ledger/src/partial_tx.rs index 9173519..7ba4bdc 100644 --- a/goas/cl/ledger/src/partial_tx.rs +++ b/goas/cl/ledger/src/partial_tx.rs @@ -1,76 +1,120 @@ use std::collections::BTreeMap; -use ledger_proof_statements::death_constraint::DeathConstraintPublic; - -use crate::{ - death_constraint::DeathProof, error::Result, input::ProvedInput, output::ProvedOutput, +use ledger_proof_statements::{ + death_constraint::DeathConstraintPublic, + ptx::{PtxPrivate, PtxPublic}, }; -#[derive(Debug, Clone)] -pub struct PartialTxInput { - pub input: ProvedInput, - pub death: DeathProof, -} +use crate::{ + death_constraint::DeathProof, error::{Error, Result} +}; + +const MAX_NOTE_COMMS: usize = 2usize.pow(8); -impl PartialTxInput { - fn verify(&self, ptx_root: cl::PtxRoot) -> bool { - let nf = self.input.input.input.nullifier; - self.input.input.input.death_cm == self.death.death_commitment() // ensure the death proof is actually for this input - && self.input.verify() // ensure the input proof is valid - && self.death.verify(DeathConstraintPublic { nf, ptx_root }) // verify the death constraint was satisfied - } -} pub struct ProvedPartialTx { - pub inputs: Vec, - pub outputs: Vec, + pub ptx: cl::PartialTx, + pub cm_root: [u8;32], + pub death_proofs: BTreeMap, + pub risc0_receipt: risc0_zkvm::Receipt, } impl ProvedPartialTx { pub fn prove( ptx: &cl::PartialTxWitness, - mut death_proofs: BTreeMap, + death_proofs: BTreeMap, note_commitments: &[cl::NoteCommitment], ) -> Result { - let inputs = ptx - .inputs - .iter() - .map(|i| { - Ok(PartialTxInput { - input: ProvedInput::prove(i, note_commitments)?, - death: death_proofs - .remove(&i.nullifier()) - .expect("Input missing death proof"), - }) - }) - .collect::>()?; + let cm_leaves = note_commitment_leaves(note_commitments); - let outputs = ptx - .outputs - .iter() - .map(ProvedOutput::prove) - .collect::>()?; + let input_cm_paths = Vec::from_iter(ptx.inputs.iter().map(|input| { + let output_cm = input.note_commitment(); - Ok(Self { inputs, outputs }) + let cm_idx = note_commitments + .iter() + .position(|c| c == &output_cm) + .unwrap(); + + cl::merkle::path(cm_leaves, cm_idx) + })); + let cm_root = cl::merkle::root(cm_leaves); + let ptx_private = PtxPrivate { + ptx: ptx.clone(), + input_cm_paths, + cm_root, + }; + + let env = risc0_zkvm::ExecutorEnv::builder() + .write(&ptx_private) + .unwrap() + .build() + .unwrap(); + + // Obtain the default prover. + let prover = risc0_zkvm::default_prover(); + + let start_t = std::time::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, nomos_cl_risc0_proofs::PTX_ELF, &opts) + .map_err(|_| Error::Risc0ProofFailed)?; + + println!( + "STARK 'ptx' prover time: {:.2?}, total_cycles: {}", + start_t.elapsed(), + prove_info.stats.total_cycles + ); + // extract the receipt. + let receipt = prove_info.receipt; + + Ok(Self { + ptx: ptx.commit(), + cm_root, + risc0_receipt: receipt, + death_proofs, + }) } - pub fn ptx(&self) -> cl::PartialTx { - cl::PartialTx { - inputs: Vec::from_iter(self.inputs.iter().map(|i| i.input.input.input)), - outputs: Vec::from_iter(self.outputs.iter().map(|o| o.output)), - } - } - - pub fn verify_inputs(&self) -> bool { - let ptx_root = self.ptx().root(); - self.inputs.iter().all(|i| i.verify(ptx_root)) - } - - pub fn verify_outputs(&self) -> bool { - self.outputs.iter().all(|o| o.verify()) + pub fn public(&self) -> Result { + Ok(self.risc0_receipt.journal.decode()?) } pub fn verify(&self) -> bool { - self.verify_inputs() && self.verify_outputs() + let Ok(proved_ptx_inputs) = self.public() else { + return false; + }; + if (PtxPublic { ptx: self.ptx.clone(), cm_root: self.cm_root }) != proved_ptx_inputs { + return false; + } + + let ptx_root = self.ptx.root(); + + for input in self.ptx.inputs.iter() { + let nf = input.nullifier; + let Some(death_proof) = self.death_proofs.get(&nf) else { + return false; + }; + if input.death_cm != death_proof.death_commitment() { + // ensure the death proof is actually for this input + return false; + } + if !death_proof.verify(DeathConstraintPublic { nf, ptx_root }) { + // verify the death constraint was satisfied + return false; + } + } + + self.risc0_receipt + .verify(nomos_cl_risc0_proofs::PTX_ID) + .is_ok() } } + +fn note_commitment_leaves(note_commitments: &[cl::NoteCommitment]) -> [[u8; 32]; MAX_NOTE_COMMS] { + let note_comm_bytes = Vec::from_iter(note_commitments.iter().map(|c| c.as_bytes().to_vec())); + let cm_leaves = cl::merkle::padded_leaves::(¬e_comm_bytes); + cm_leaves +} diff --git a/goas/cl/ledger/tests/simple_transfer.rs b/goas/cl/ledger/tests/simple_transfer.rs index 79b70a3..ad3c05c 100644 --- a/goas/cl/ledger/tests/simple_transfer.rs +++ b/goas/cl/ledger/tests/simple_transfer.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use cl::note::unit_point; +use cl::{note::unit_point, BalanceWitness}; use ledger::{bundle::ProvedBundle, death_constraint::DeathProof, partial_tx::ProvedPartialTx}; use rand_core::CryptoRngCore; @@ -45,7 +45,7 @@ fn test_simple_transfer() { alice.pk(), &mut rng, ); - let alices_input = cl::InputWitness::random(utxo, alice.sk(), &mut rng); + let alices_input = cl::InputWitness::from_output(utxo, alice.sk()); // Alice wants to send 8 NMO to bob let bobs_output = cl::OutputWitness::random(cl::NoteWitness::basic(8, nmo), bob.pk(), &mut rng); @@ -58,6 +58,7 @@ fn test_simple_transfer() { let ptx_witness = cl::PartialTxWitness { inputs: vec![alices_input], outputs: vec![bobs_output, change_output], + balance_blinding: BalanceWitness::random(&mut rng), }; // Prove the death constraints for alices input (she uses the no-op death constraint) @@ -79,7 +80,7 @@ fn test_simple_transfer() { }; let bundle_witness = cl::BundleWitness { - balance_blinding: ptx_witness.balance_blinding(), + balance_blinding: ptx_witness.balance_blinding, }; let proved_bundle = ProvedBundle::prove(&bundle, &bundle_witness).unwrap(); diff --git a/goas/cl/ledger_proof_statements/src/input.rs b/goas/cl/ledger_proof_statements/src/input.rs deleted file mode 100644 index 20dab89..0000000 --- a/goas/cl/ledger_proof_statements/src/input.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// for public inputs `nf` (nullifier), `root_cm` (root of merkle tree over commitment set) and `death_cm` (commitment to death constraint). -/// 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) -/// 4. death_cm = death_commitment(note.death_constraint) - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct InputPublic { - pub cm_root: [u8; 32], - pub input: cl::Input, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct InputPrivate { - pub input: cl::InputWitness, - pub cm_path: Vec, -} diff --git a/goas/cl/ledger_proof_statements/src/lib.rs b/goas/cl/ledger_proof_statements/src/lib.rs index 1029da4..4845666 100644 --- a/goas/cl/ledger_proof_statements/src/lib.rs +++ b/goas/cl/ledger_proof_statements/src/lib.rs @@ -1,2 +1,2 @@ pub mod death_constraint; -pub mod input; +pub mod ptx; diff --git a/goas/cl/ledger_proof_statements/src/ptx.rs b/goas/cl/ledger_proof_statements/src/ptx.rs new file mode 100644 index 0000000..c65eab6 --- /dev/null +++ b/goas/cl/ledger_proof_statements/src/ptx.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; +use cl::{PartialTx, PartialTxWitness}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PtxPublic { + pub ptx: PartialTx, + pub cm_root: [u8; 32], +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PtxPrivate { + pub ptx: PartialTxWitness, + pub input_cm_paths: Vec>, + pub cm_root: [u8; 32], +} diff --git a/goas/cl/risc0_proofs/Cargo.toml b/goas/cl/risc0_proofs/Cargo.toml index 7a77cd5..d53a6fd 100644 --- a/goas/cl/risc0_proofs/Cargo.toml +++ b/goas/cl/risc0_proofs/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" risc0-build = { version = "1.0" } [package.metadata.risc0] -methods = ["input", "output", "bundle", "death_constraint_nop"] +methods = ["bundle", "death_constraint_nop", "ptx"] diff --git a/goas/cl/risc0_proofs/input/src/main.rs b/goas/cl/risc0_proofs/input/src/main.rs deleted file mode 100644 index ada2145..0000000 --- a/goas/cl/risc0_proofs/input/src/main.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// Input Proof -use cl::merkle; -use ledger_proof_statements::input::{InputPrivate, InputPublic}; -use risc0_zkvm::guest::env; - -fn main() { - let secret: InputPrivate = env::read(); - - let out_cm = secret.input.note_commitment(); - let cm_leaf = merkle::leaf(out_cm.as_bytes()); - let cm_root = merkle::path_root(cm_leaf, &secret.cm_path); - - env::commit(&InputPublic { - input: secret.input.commit(), - cm_root, - }); -} diff --git a/goas/cl/risc0_proofs/output/Cargo.toml b/goas/cl/risc0_proofs/output/Cargo.toml deleted file mode 100644 index e33aea6..0000000 --- a/goas/cl/risc0_proofs/output/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "output" -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/goas/cl/risc0_proofs/output/src/main.rs b/goas/cl/risc0_proofs/output/src/main.rs deleted file mode 100644 index 2674230..0000000 --- a/goas/cl/risc0_proofs/output/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// Output Proof -/// -/// given randomness `r` and `note=(value, unit, ...)` prove that -/// - balance = balance_commit(value, unit, r) -/// - note_cm = note_commit(note) -use risc0_zkvm::guest::env; - -fn main() { - let output: cl::OutputWitness = env::read(); - - // 0 does not contribute to balance, implications of this are unclear - // therefore out of an abundance of caution, we disallow these zero - // valued "dummy notes". - assert!(output.note.value > 0); - - let output_cm = output.commit(); - env::commit(&output_cm); -} diff --git a/goas/cl/risc0_proofs/input/Cargo.toml b/goas/cl/risc0_proofs/ptx/Cargo.toml similarity index 97% rename from goas/cl/risc0_proofs/input/Cargo.toml rename to goas/cl/risc0_proofs/ptx/Cargo.toml index fd1f63f..181aef7 100644 --- a/goas/cl/risc0_proofs/input/Cargo.toml +++ b/goas/cl/risc0_proofs/ptx/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "input" +name = "ptx" version = "0.1.0" edition = "2021" diff --git a/goas/cl/risc0_proofs/ptx/src/main.rs b/goas/cl/risc0_proofs/ptx/src/main.rs new file mode 100644 index 0000000..4b3a251 --- /dev/null +++ b/goas/cl/risc0_proofs/ptx/src/main.rs @@ -0,0 +1,28 @@ +/// Input Proof +use cl::merkle; +use ledger_proof_statements::ptx::{PtxPrivate, PtxPublic}; +use risc0_zkvm::guest::env; + +fn main() { + let PtxPrivate { + ptx, + input_cm_paths, + cm_root, + } = env::read(); + + assert_eq!(ptx.inputs.len(), input_cm_paths.len()); + for (input, cm_path) in ptx.inputs.iter().zip(input_cm_paths) { + let note_cm = input.note_commitment(); + let cm_leaf = merkle::leaf(note_cm.as_bytes()); + assert_eq!(cm_root, merkle::path_root(cm_leaf, &cm_path)); + } + + for output in ptx.outputs.iter() { + assert!(output.note.value > 0); + } + + env::commit(&PtxPublic { + ptx: ptx.commit(), + cm_root, + }); +}