diff --git a/goas/atomic_asset_transfer/common/src/lib.rs b/goas/atomic_asset_transfer/common/src/lib.rs index f0cfd2a..db5f305 100644 --- a/goas/atomic_asset_transfer/common/src/lib.rs +++ b/goas/atomic_asset_transfer/common/src/lib.rs @@ -1,4 +1,4 @@ -use cl::{balance::Unit, merkle, nullifier::NullifierCommitment}; +use cl::{balance::Unit, merkle, PartialTxInputWitness}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -62,31 +62,35 @@ impl StateWitness { } pub fn withdraw(mut self, w: Withdraw) -> Self { - self.included_txs.push(Tx::Withdraw(w)); - let Withdraw { from, amount, - to: _, - } = w; + bind: _, + } = &w; - let from_balance = self.balances.entry(from).or_insert(0); + let from_balance = self.balances.entry(*from).or_insert(0); *from_balance = from_balance - .checked_sub(amount) + .checked_sub(*amount) .expect("insufficient funds in account"); + self.included_txs.push(Tx::Withdraw(w)); + self } pub fn deposit(mut self, d: Deposit) -> Self { - self.included_txs.push(Tx::Deposit(d)); + let Deposit { + to, + amount, + bind: _, + } = &d; - let Deposit { to, amount } = d; - - let to_balance = self.balances.entry(to).or_insert(0); + let to_balance = self.balances.entry(*to).or_insert(0); *to_balance += to_balance - .checked_add(amount) + .checked_add(*amount) .expect("overflow in account balance"); + + self.included_txs.push(Tx::Deposit(d)); self } @@ -144,40 +148,42 @@ impl From for [u8; 32] { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Withdraw { pub from: AccountId, pub amount: u64, - pub to: NullifierCommitment, + pub bind: PartialTxInputWitness, } impl Withdraw { - pub fn to_bytes(&self) -> [u8; 44] { - let mut bytes = [0; 44]; + pub fn to_bytes(&self) -> [u8; 108] { + let mut bytes = [0; 108]; bytes[0..4].copy_from_slice(&self.from.to_le_bytes()); bytes[4..12].copy_from_slice(&self.amount.to_le_bytes()); - bytes[12..44].copy_from_slice(self.to.as_bytes()); + bytes[12..108].copy_from_slice(&self.bind.input.commit().to_bytes()); bytes } } /// A deposit of funds into the zone -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Deposit { pub to: AccountId, pub amount: u64, + pub bind: PartialTxInputWitness, } impl Deposit { - pub fn to_bytes(&self) -> [u8; 32] { - let mut bytes = [0; 32]; + pub fn to_bytes(&self) -> [u8; 108] { + let mut bytes = [0; 108]; bytes[0..4].copy_from_slice(&self.to.to_le_bytes()); bytes[4..12].copy_from_slice(&self.amount.to_le_bytes()); + bytes[12..108].copy_from_slice(&self.bind.input.commit().to_bytes()); bytes } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Tx { Withdraw(Withdraw), Deposit(Deposit), diff --git a/goas/atomic_asset_transfer/executor/src/lib.rs b/goas/atomic_asset_transfer/executor/src/lib.rs index 5195576..2def1b1 100644 --- a/goas/atomic_asset_transfer/executor/src/lib.rs +++ b/goas/atomic_asset_transfer/executor/src/lib.rs @@ -1,6 +1,5 @@ use common::{StateWitness, Tx}; use goas_proof_statements::{zone_funds::SpendFundsPrivate, zone_state::ZoneStatePrivate}; -use std::collections::VecDeque; pub fn prove_zone_stf( state: StateWitness, @@ -8,8 +7,6 @@ pub fn prove_zone_stf( zone_in: cl::PartialTxInputWitness, zone_out: cl::PartialTxOutputWitness, funds_out: cl::PartialTxOutputWitness, - withdrawals: VecDeque, - deposits: VecDeque, ) -> ledger::DeathProof { let private_inputs = ZoneStatePrivate { state, @@ -17,8 +14,6 @@ pub fn prove_zone_stf( zone_in, zone_out, funds_out, - withdrawals, - deposits, }; let env = risc0_zkvm::ExecutorEnv::builder() diff --git a/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs b/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs index 6cdab01..8da5e23 100644 --- a/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs +++ b/goas/atomic_asset_transfer/executor/tests/withdraw_ptx.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, VecDeque}; +use std::collections::BTreeMap; use cl::{NoteWitness, NullifierNonce, NullifierSecret}; use common::{StateWitness, Tx, ZoneMetadata, ZONE_CL_FUNDS_UNIT}; @@ -65,13 +65,28 @@ fn test_withdrawal() { cl::InputWitness::public(zone_fund_utxo(100, init_state.zone_metadata, &mut rng)); let zone_state_in = cl::InputWitness::public(zone_state_utxo(&init_state, &mut rng)); + let alice_intent = cl::InputWitness::random( + cl::OutputWitness::random( + NoteWitness::stateless(1, *ZONE_CL_FUNDS_UNIT, DeathProof::nop_constraint()), // TODO, intent should be in the death constraint + alice_sk.commit(), + &mut rng, + ), + alice_sk, + &mut rng, + ); + + let mut withdraw_ptx = cl::PartialTxWitness { + inputs: vec![zone_state_in, zone_fund_in, alice_intent], + outputs: vec![], + }; + let withdraw = common::Withdraw { from: alice, amount: 78, - to: alice_sk.commit(), + bind: withdraw_ptx.input_witness(2), }; - let end_state = init_state.clone().withdraw(withdraw).evolve_nonce(); + let end_state = init_state.clone().withdraw(withdraw.clone()).evolve_nonce(); let zone_state_out = cl::OutputWitness::public( cl::NoteWitness { @@ -98,22 +113,17 @@ fn test_withdrawal() { &mut rng, ); - let withdraw_ptx = cl::PartialTxWitness { - inputs: vec![zone_state_in, zone_fund_in], - outputs: vec![zone_state_out, zone_fund_out, alice_withdrawal], - }; + withdraw_ptx.outputs = vec![zone_state_out, zone_fund_out, alice_withdrawal]; let death_proofs = BTreeMap::from_iter([ ( zone_state_in.nullifier(), executor::prove_zone_stf( init_state.clone(), - vec![Tx::Withdraw(withdraw)], + vec![Tx::Withdraw(withdraw.clone())], withdraw_ptx.input_witness(0), // input state note (input #0) withdraw_ptx.output_witness(0), // output state note (output #0) withdraw_ptx.output_witness(1), // output funds note (output #1) - VecDeque::from_iter([withdraw_ptx.output_witness(2)]), // alice withdrawal - VecDeque::new(), // no deposits ), ), ( @@ -124,11 +134,18 @@ fn test_withdrawal() { &end_state, ), ), + ( + alice_intent.nullifier(), + DeathProof::prove_nop(alice_intent.nullifier(), withdraw_ptx.commit().root()), + ), ]); + println!("done"); + let note_commitments = vec![ zone_state_in.note_commitment(), zone_fund_in.note_commitment(), + alice_intent.note_commitment(), ]; let withdraw_proof = diff --git a/goas/atomic_asset_transfer/proof_statements/src/zone_state.rs b/goas/atomic_asset_transfer/proof_statements/src/zone_state.rs index e67688d..2966b48 100644 --- a/goas/atomic_asset_transfer/proof_statements/src/zone_state.rs +++ b/goas/atomic_asset_transfer/proof_statements/src/zone_state.rs @@ -1,6 +1,5 @@ use common::{StateWitness, Tx}; use serde::{Deserialize, Serialize}; -use std::collections::VecDeque; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ZoneStatePrivate { @@ -13,8 +12,4 @@ pub struct ZoneStatePrivate { /// This means that while there's nothing to prevent creation of notes with the same characteristics of zone /// funds, those would not be tracked by the zone state and can be ignored. pub funds_out: cl::PartialTxOutputWitness, - /// Each note is the result of the execution of a withdrawal request - pub withdrawals: VecDeque, - /// Each note is providing funds for a deposit request - pub deposits: VecDeque, } 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 e5e9597..d913b2b 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 @@ -1,58 +1,18 @@ -use cl::{ - note::NoteWitness, - nullifier::NullifierNonce, - output::OutputWitness, - partial_tx::{PartialTxInputWitness, PartialTxOutputWitness}, - PtxRoot, -}; +use cl::{note::NoteWitness, nullifier::NullifierNonce, output::OutputWitness, PtxRoot}; use common::*; use goas_proof_statements::zone_state::ZoneStatePrivate; use ledger_proof_statements::death_constraint::DeathConstraintPublic; use risc0_zkvm::guest::env; -fn withdraw( - state: StateWitness, - output_root: [u8; 32], - withdrawal_req: Withdraw, - withdrawal: PartialTxOutputWitness, -) -> StateWitness { - // 1) check the correct amount of funds is being spent - assert_eq!(withdrawal.output.note.value, withdrawal_req.amount); - assert_eq!(withdrawal.output.note.unit, *ZONE_CL_FUNDS_UNIT); - // 2) check the correct recipient is being paid - assert_eq!(withdrawal.output.nf_pk, withdrawal_req.to); - - assert_eq!(output_root, withdrawal.output_root()); - - state.withdraw(withdrawal_req) +fn withdraw(state: StateWitness, input_root: [u8; 32], withdrawal: Withdraw) -> StateWitness { + assert_eq!(input_root, withdrawal.bind.input_root()); + state.withdraw(withdrawal) } -fn deposit( - state: StateWitness, - input_root: [u8; 32], - deposit_req: Deposit, - deposit: PartialTxInputWitness, -) -> StateWitness { - assert_eq!(deposit.input_root(), input_root); - - // 1) Check the deposit note is not already under control of the zone - assert_ne!( - deposit.input.note.death_constraint, - state.zone_metadata.funds_vk - ); - - // 2) Check the deposit note is for the correct amount - assert_eq!(deposit.input.note.unit, *ZONE_CL_FUNDS_UNIT); - assert_eq!(deposit.input.note.value, deposit_req.amount); - - // 3) Check the deposit note is for the correct recipient - assert_eq!( - AccountId::from_le_bytes(<[u8; 4]>::try_from(&deposit.input.note.state[..4]).unwrap()), - deposit_req.to - ); - - state.deposit(deposit_req) +fn deposit(state: StateWitness, input_root: [u8; 32], deposit: Deposit) -> StateWitness { + assert_eq!(deposit.bind.input_root(), input_root); + state.deposit(deposit) } fn validate_zone_transition( @@ -113,8 +73,6 @@ fn main() { zone_in, zone_out, funds_out, - mut withdrawals, - mut deposits, } = env::read(); let input_root = zone_in.input_root(); @@ -129,8 +87,8 @@ fn main() { for input in inputs { state = match input { - Tx::Withdraw(w) => withdraw(state, output_root, w, withdrawals.pop_front().unwrap()), - Tx::Deposit(d) => deposit(state, input_root, d, deposits.pop_front().unwrap()), + Tx::Withdraw(w) => withdraw(state, input_root, w), + Tx::Deposit(d) => deposit(state, input_root, d), } }