diff --git a/goas/atomic_asset_transfer/common/src/lib.rs b/goas/atomic_asset_transfer/common/src/lib.rs index 6623080..852bdcf 100644 --- a/goas/atomic_asset_transfer/common/src/lib.rs +++ b/goas/atomic_asset_transfer/common/src/lib.rs @@ -26,7 +26,7 @@ pub static ZONE_CL_FUNDS_UNIT: Lazy = Lazy::new(|| crypto::hash_to_curve(b #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ZoneMetadata { - pub self_vk: [u8; 32], + pub zone_vk: [u8; 32], pub funds_vk: [u8; 32], pub unit: Unit, } @@ -34,7 +34,7 @@ pub struct ZoneMetadata { impl ZoneMetadata { pub fn id(&self) -> [u8; 32] { let mut hasher = Sha256::new(); - hasher.update(&self.self_vk); + hasher.update(&self.zone_vk); hasher.update(&self.funds_vk); hasher.update(self.unit.compress().as_bytes()); hasher.finalize().into() @@ -50,6 +50,12 @@ pub struct StateWitness { } impl StateWitness { + /// Merkle tree over: + /// root + /// / \ + /// io state + /// / \ / \ + /// events txs zoneid balances pub fn commit(&self) -> StateCommitment { let io_root = cl::merkle::node(self.events_root(), self.included_txs_root()); diff --git a/goas/atomic_asset_transfer/proof_statements/src/zone_funds.rs b/goas/atomic_asset_transfer/proof_statements/src/zone_funds.rs index c44d8db..0343902 100644 --- a/goas/atomic_asset_transfer/proof_statements/src/zone_funds.rs +++ b/goas/atomic_asset_transfer/proof_statements/src/zone_funds.rs @@ -34,6 +34,10 @@ pub struct SpendFundsPrivate { pub spent_note: PartialTxOutputPrivate, /// The event emitted by the zone that authorizes the spend pub spend_event: Spend, - /// Path to the zone output state + /// Path to the zone output events root pub spend_event_state_path: Vec, + /// Merkle root of txs included in the zone + pub txs_root: [u8; 32], + /// Merkle root of balances in the zone + pub balances_root: [u8; 32], } diff --git a/goas/atomic_asset_transfer/risc0_proofs/spend_zone_funds/src/main.rs b/goas/atomic_asset_transfer/risc0_proofs/spend_zone_funds/src/main.rs index 42f7f51..9c24800 100644 --- a/goas/atomic_asset_transfer/risc0_proofs/spend_zone_funds/src/main.rs +++ b/goas/atomic_asset_transfer/risc0_proofs/spend_zone_funds/src/main.rs @@ -16,21 +16,35 @@ fn main() { spent_note, spend_event, spend_event_state_path, + txs_root, + balances_root, } = env::read(); let ptx_root = in_zone_funds.ptx_root(); let nf = Nullifier::new(in_zone_funds.input.nf_sk, in_zone_funds.input.nonce); // check the zone funds note is the one in the spend event assert_eq!(nf, spend_event.nf); - assert_eq!(ptx_root, zone_note.ptx_root()); - // assert the spent event was an output of the zone stf + + // ** Assert the spent event was an output of the correct zone stf ** + // The zone state field is a merkle tree over: + // root + // / \ + // io state + // / \ / \ + // events txs zoneid balances + // We need to check that: + // 1) There is a valid path from the spend event to the events root + // 2) The zone id matches the one in the current funds note state + // 3) The witnesses for spend path, txs and balances allow to calculate the correct root + let zone_id = in_zone_funds.input.note.state; // TODO: is there more state? let spend_event_leaf = merkle::leaf(&spend_event.to_bytes()); - // TODO: zones will have some more state - assert_eq!( - zone_note.output.note.state, - merkle::path_root(spend_event_leaf, &spend_event_state_path) - ); + let event_root = merkle::path_root(spend_event_leaf, &spend_event_state_path); + + let io_root = merkle::node(event_root, txs_root); + let state_root = merkle::node(zone_id, balances_root); + let root = merkle::node(io_root, state_root); + assert_eq!(root, zone_note.output.note.state); assert_eq!(ptx_root, out_zone_funds.ptx_root()); @@ -64,6 +78,11 @@ fn main() { out_zone_funds.output.nonce, NullifierNonce::from_bytes(Sha256::digest(&out_zone_funds.output.nonce.as_bytes()).into()) ); + // the state is propagated + assert_eq!( + out_zone_funds.output.note.state, + in_zone_funds.input.note.state + ); assert_eq!(ptx_root, spent_note.ptx_root()); 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 c4541bf..f1c6ef8 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 @@ -93,6 +93,8 @@ fn deposit( // 4) Check the zone fund notes are correctly created assert_eq!(zone_funds_in.note.death_constraint, funds_vk); assert_eq!(zone_funds_out.note.death_constraint, funds_vk); + assert_eq!(zone_funds_in.note.state, state.zone_metadata.id()); + assert_eq!(zone_funds_out.note.state, state.zone_metadata.id()); assert_eq!(zone_funds_in.nf_sk, NullifierSecret::from_bytes([0; 16])); // there is no secret in the zone funds assert_eq!(zone_funds_out.nf_pk, zone_funds_in.nf_sk.commit()); // the sk is the same // nonce is correctly evolved @@ -141,7 +143,7 @@ fn validate_zone_input( // should not be possible to create one but let's put this check here just in case debug_assert_eq!( input.input.note.death_constraint, - state.zone_metadata.self_vk + state.zone_metadata.zone_vk ); (ptx_root, nf) @@ -156,7 +158,7 @@ fn validate_zone_output( assert_eq!(ptx, output.ptx_root()); // the ptx root is the same as in the input let output = output.output; assert_eq!(output.note.state, <[u8; 32]>::from(state.commit())); // the state in the output is as calculated by this function - assert_eq!(output.note.death_constraint, state.zone_metadata.self_vk); // the death constraint is the correct one + assert_eq!(output.note.death_constraint, state.zone_metadata.zone_vk); // the death constraint is the correct one assert_eq!(output.nf_pk, NullifierSecret::from_bytes([0; 16]).commit()); // the nullifier secret is public assert_eq!(output.balance_blinding, input.balance_blinding); // the balance blinding is the same as in the input assert_eq!(output.note.unit, state.zone_metadata.unit); // the balance unit is the same as in the input