From 341c8b44d35184565792cde94b6833ed3674e69b Mon Sep 17 00:00:00 2001 From: David Rusu Date: Mon, 19 Aug 2024 19:29:43 +0400 Subject: [PATCH 1/4] goas: isolated test for zone stf proof --- .../atomic_asset_transfer/executor/src/lib.rs | 76 +++++++++++++++++++ goas/cl/cl/src/nullifier.rs | 4 +- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/goas/atomic_asset_transfer/executor/src/lib.rs b/goas/atomic_asset_transfer/executor/src/lib.rs index 3bb8de4..7a299a9 100644 --- a/goas/atomic_asset_transfer/executor/src/lib.rs +++ b/goas/atomic_asset_transfer/executor/src/lib.rs @@ -214,3 +214,79 @@ pub fn prove_user_atomic_transfer(atomic_transfer: UserAtomicTransfer) -> ledger let receipt = prove_info.receipt; ledger::DeathProof::from_risc0(goas_risc0_proofs::USER_ATOMIC_TRANSFER_ID, receipt) } + +#[cfg(test)] +mod tests { + use cl::{NoteWitness, OutputWitness, PartialTxWitness}; + use common::{BoundTx, Deposit, Withdraw}; + use ledger_proof_statements::death_constraint::DeathConstraintPublic; + + use super::*; + + #[test] + pub fn test_prove_zone_stf() { + let mut rng = rand::thread_rng(); + + let zone_start = ZoneNotes::new_with_balances("ZONE", BTreeMap::from_iter([]), &mut rng); + + let bind = OutputWitness::public( + NoteWitness::basic(32, *common::ZONE_CL_FUNDS_UNIT), + cl::NullifierNonce([0u8; 32]), + ); + + let mut alice = common::new_account(&mut rng); + let alice_vk = alice.verifying_key().to_bytes(); + + let signed_deposit = SignedBoundTx::sign( + BoundTx { + tx: Tx::Deposit(Deposit { + to: alice_vk, + amount: 32, + }), + bind: bind.commit_note(), + }, + &mut alice, + ); + let signed_withdraw = SignedBoundTx::sign( + BoundTx { + tx: Tx::Withdraw(Withdraw { + from: alice_vk, + amount: 10, + }), + bind: bind.commit_note(), + }, + &mut alice, + ); + + let zone_end = zone_start + .clone() + .run([signed_deposit.bound_tx.tx, signed_withdraw.bound_tx.tx]); + + let ptx = PartialTxWitness { + inputs: vec![ + cl::InputWitness::public(bind), + zone_start.state_input_witness(), + zone_start.fund_input_witness(), + ], + outputs: vec![zone_end.state_note, zone_end.fund_note], + }; + + let txs = vec![ + (signed_deposit, ptx.input_witness(0)), + (signed_withdraw, ptx.input_witness(0)), + ]; + + let proof = prove_zone_stf( + zone_start.state.clone(), + txs, + ptx.input_witness(1), + ptx.output_witness(0), + ptx.output_witness(1), + ); + + assert!(proof.verify(DeathConstraintPublic { + nf: zone_start.state_input_witness().nullifier(), + ptx_root: ptx.commit().root(), + })) + } +} diff --git a/goas/cl/cl/src/nullifier.rs b/goas/cl/cl/src/nullifier.rs index ada9f81..295b674 100644 --- a/goas/cl/cl/src/nullifier.rs +++ b/goas/cl/cl/src/nullifier.rs @@ -25,13 +25,13 @@ pub struct NullifierSecret(pub [u8; 16]); // can be provided to anyone wishing to transfer // you a note #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct NullifierCommitment([u8; 32]); +pub struct NullifierCommitment(pub [u8; 32]); // To allow users to maintain fewer nullifier secrets, we // provide a nonce to differentiate notes controlled by the same // secret. Each note is assigned a unique nullifier nonce. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct NullifierNonce([u8; 32]); +pub struct NullifierNonce(pub [u8; 32]); // The nullifier attached to input notes to prove an input has not // already been spent. From a4e52b75aaccbfdc0b4a37ad644055b8d2859405 Mon Sep 17 00:00:00 2001 From: David Rusu Date: Mon, 19 Aug 2024 20:46:48 +0400 Subject: [PATCH 2/4] goas: add isolated test for zone fund constraint --- .../atomic_asset_transfer/executor/src/lib.rs | 21 ++++++++++++++++++- .../executor/tests/atomic_transfer.rs | 4 ++-- .../executor/tests/withdraw_ptx.rs | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/goas/atomic_asset_transfer/executor/src/lib.rs b/goas/atomic_asset_transfer/executor/src/lib.rs index 7a299a9..d0f0779 100644 --- a/goas/atomic_asset_transfer/executor/src/lib.rs +++ b/goas/atomic_asset_transfer/executor/src/lib.rs @@ -157,7 +157,7 @@ pub fn prove_zone_stf( ledger::DeathProof::from_risc0(goas_risc0_proofs::ZONE_STATE_ID, receipt) } -pub fn prove_zone_fund_withdraw( +pub fn prove_zone_fund_constraint( in_zone_funds: cl::PartialTxInputWitness, zone_note: cl::PartialTxOutputWitness, out_zone_state: &StateWitness, @@ -289,4 +289,23 @@ mod tests { ptx_root: ptx.commit().root(), })) } + + #[test] + fn test_prove_zone_fund_constraint() { + let zone = + ZoneNotes::new_with_balances("ZONE", BTreeMap::from_iter([]), &mut rand::thread_rng()); + + let ptx = PartialTxWitness { + inputs: vec![zone.fund_input_witness()], + outputs: vec![zone.state_note], + }; + + let proof = + prove_zone_fund_constraint(ptx.input_witness(0), ptx.output_witness(0), &zone.state); + + assert!(proof.verify(DeathConstraintPublic { + nf: zone.fund_input_witness().nullifier(), + ptx_root: ptx.commit().root(), + })) + } } diff --git a/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs b/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs index ac66029..975831d 100644 --- a/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs +++ b/goas/atomic_asset_transfer/executor/tests/atomic_transfer.rs @@ -111,7 +111,7 @@ fn test_atomic_transfer() { ), ( zone_a_start.fund_input_witness().nullifier(), - executor::prove_zone_fund_withdraw( + executor::prove_zone_fund_constraint( atomic_transfer_ptx.input_witness(2), // input fund note atomic_transfer_ptx.output_witness(0), // output state note &zone_a_end.state, @@ -129,7 +129,7 @@ fn test_atomic_transfer() { ), ( zone_b_start.fund_input_witness().nullifier(), - executor::prove_zone_fund_withdraw( + executor::prove_zone_fund_constraint( atomic_transfer_ptx.input_witness(4), // input fund note (input #1) atomic_transfer_ptx.output_witness(2), // output state note (output #0) &zone_b_end.state, diff --git a/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs b/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs index ae189c4..5f099d1 100644 --- a/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs +++ b/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs @@ -73,7 +73,7 @@ fn test_withdrawal() { ), ( zone_start.fund_input_witness().nullifier(), - executor::prove_zone_fund_withdraw( + executor::prove_zone_fund_constraint( withdraw_ptx.input_witness(1), // input fund note (input #1) withdraw_ptx.output_witness(0), // output state note (output #0) &zone_end.state, From 6862f291efd708773359c0021d6fca0026890e7f Mon Sep 17 00:00:00 2001 From: David Rusu Date: Mon, 19 Aug 2024 21:11:50 +0400 Subject: [PATCH 3/4] goas: isolated test for user atomic transfer proof --- .../atomic_asset_transfer/executor/src/lib.rs | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/goas/atomic_asset_transfer/executor/src/lib.rs b/goas/atomic_asset_transfer/executor/src/lib.rs index d0f0779..1b7e57a 100644 --- a/goas/atomic_asset_transfer/executor/src/lib.rs +++ b/goas/atomic_asset_transfer/executor/src/lib.rs @@ -217,8 +217,9 @@ pub fn prove_user_atomic_transfer(atomic_transfer: UserAtomicTransfer) -> ledger #[cfg(test)] mod tests { - use cl::{NoteWitness, OutputWitness, PartialTxWitness}; + use cl::{note::unit_point, NoteWitness, NullifierNonce, OutputWitness, PartialTxWitness}; use common::{BoundTx, Deposit, Withdraw}; + use goas_proof_statements::user_note::UserIntent; use ledger_proof_statements::death_constraint::DeathConstraintPublic; use super::*; @@ -308,4 +309,59 @@ mod tests { ptx_root: ptx.commit().root(), })) } + + #[test] + fn test_prove_user_atomic_transfer() { + let mut rng = rand::thread_rng(); + + let alice = common::new_account(&mut rng); + let alice_vk = alice.verifying_key().to_bytes(); + + let mut zone_a = + ZoneNotes::new_with_balances("ZONE_A", BTreeMap::from_iter([(alice_vk, 40)]), &mut rng); + let mut zone_b = ZoneNotes::new_with_balances("ZONE_B", BTreeMap::new(), &mut rng); + + let user_intent = UserIntent { + zone_a_meta: zone_a.state.zone_metadata, + zone_b_meta: zone_b.state.zone_metadata, + withdraw: Withdraw { + from: alice_vk, + amount: 32, + }, + deposit: Deposit { + to: alice_vk, + amount: 32, + }, + }; + let user_note = cl::InputWitness::public(cl::OutputWitness::public( + NoteWitness::new(1, unit_point("INTENT"), [0u8; 32], user_intent.commit()), + NullifierNonce::random(&mut rng), + )); + + zone_a = zone_a.run([Tx::Withdraw(user_intent.withdraw)]); + zone_b = zone_b.run([Tx::Deposit(user_intent.deposit)]); + + let ptx = PartialTxWitness { + inputs: vec![user_note], + outputs: vec![zone_a.state_note, zone_b.state_note], + }; + + let user_atomic_transfer = UserAtomicTransfer { + user_note: ptx.input_witness(0), + user_intent, + zone_a: ptx.output_witness(0), + zone_b: ptx.output_witness(1), + zone_a_roots: zone_a.state.state_roots(), + zone_b_roots: zone_b.state.state_roots(), + withdraw_tx: zone_a.state.included_tx_witness(0), + deposit_tx: zone_b.state.included_tx_witness(0), + }; + + let proof = prove_user_atomic_transfer(user_atomic_transfer); + + assert!(proof.verify(DeathConstraintPublic { + nf: user_note.nullifier(), + ptx_root: ptx.commit().root(), + })) + } } From e38b76e020a7a07f259d73449fc97ed4aabe2cb6 Mon Sep 17 00:00:00 2001 From: David Rusu Date: Mon, 19 Aug 2024 21:52:33 +0400 Subject: [PATCH 4/4] goas: don't expose nullifier nonce/commitment bytes --- goas/atomic_asset_transfer/executor/src/lib.rs | 2 +- goas/cl/cl/src/nullifier.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goas/atomic_asset_transfer/executor/src/lib.rs b/goas/atomic_asset_transfer/executor/src/lib.rs index 1b7e57a..7e4937f 100644 --- a/goas/atomic_asset_transfer/executor/src/lib.rs +++ b/goas/atomic_asset_transfer/executor/src/lib.rs @@ -232,7 +232,7 @@ mod tests { let bind = OutputWitness::public( NoteWitness::basic(32, *common::ZONE_CL_FUNDS_UNIT), - cl::NullifierNonce([0u8; 32]), + cl::NullifierNonce::random(&mut rng), ); let mut alice = common::new_account(&mut rng); diff --git a/goas/cl/cl/src/nullifier.rs b/goas/cl/cl/src/nullifier.rs index 295b674..ada9f81 100644 --- a/goas/cl/cl/src/nullifier.rs +++ b/goas/cl/cl/src/nullifier.rs @@ -25,13 +25,13 @@ pub struct NullifierSecret(pub [u8; 16]); // can be provided to anyone wishing to transfer // you a note #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct NullifierCommitment(pub [u8; 32]); +pub struct NullifierCommitment([u8; 32]); // To allow users to maintain fewer nullifier secrets, we // provide a nonce to differentiate notes controlled by the same // secret. Each note is assigned a unique nullifier nonce. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct NullifierNonce(pub [u8; 32]); +pub struct NullifierNonce([u8; 32]); // The nullifier attached to input notes to prove an input has not // already been spent.