From e7ba67d09bf61842587e27a0dd8fb73d06f9720c Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Fri, 2 Aug 2024 14:42:13 +0200 Subject: [PATCH 1/5] add death constraints to metadata --- goas/atomic_asset_transfer/common/Cargo.toml | 3 +- goas/atomic_asset_transfer/common/src/lib.rs | 36 ++++++++++++++----- .../risc0_proofs/zone_state/src/main.rs | 23 +++++++----- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/goas/atomic_asset_transfer/common/Cargo.toml b/goas/atomic_asset_transfer/common/Cargo.toml index 3fdc733..8c428bf 100644 --- a/goas/atomic_asset_transfer/common/Cargo.toml +++ b/goas/atomic_asset_transfer/common/Cargo.toml @@ -8,4 +8,5 @@ serde = { version = "1", features = ["derive"] } cl = { path = "../../cl/cl" } goas_proof_statements = { path = "../proof_statements" } proof_statements = { path = "../../cl/proof_statements" } -once_cell = "1" \ No newline at end of file +once_cell = "1" +sha2 = "0.10" \ No newline at end of file diff --git a/goas/atomic_asset_transfer/common/src/lib.rs b/goas/atomic_asset_transfer/common/src/lib.rs index a4bfbe8..6623080 100644 --- a/goas/atomic_asset_transfer/common/src/lib.rs +++ b/goas/atomic_asset_transfer/common/src/lib.rs @@ -7,6 +7,7 @@ use cl::{ }; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; use std::collections::BTreeMap; // TODO: sparse merkle tree @@ -20,27 +21,44 @@ pub struct StateCommitment([u8; 32]); pub type AccountId = u32; -// PLACEHOLDER: replace with the death constraint vk of the zone funds -pub const ZONE_FUNDS_VK: [u8; 32] = [0; 32]; // PLACEHOLDER: this is probably going to be NMO? pub static ZONE_CL_FUNDS_UNIT: Lazy = Lazy::new(|| crypto::hash_to_curve(b"NMO")); -// PLACEHOLDER -pub static ZONE_UNIT: Lazy = Lazy::new(|| crypto::hash_to_curve(b"ZONE_UNIT")); -// PLACEHOLDER -pub const ZONE_NF_PK: NullifierCommitment = NullifierCommitment::from_bytes([0; 32]); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ZoneMetadata { + pub self_vk: [u8; 32], + pub funds_vk: [u8; 32], + pub unit: Unit, +} + +impl ZoneMetadata { + pub fn id(&self) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(&self.self_vk); + hasher.update(&self.funds_vk); + hasher.update(self.unit.compress().as_bytes()); + hasher.finalize().into() + } +} #[derive(Clone, Serialize, Deserialize)] pub struct StateWitness { pub balances: BTreeMap, pub included_txs: Vec, pub output_events: Vec, + pub zone_metadata: ZoneMetadata, } impl StateWitness { pub fn commit(&self) -> StateCommitment { - let root = self.balances_root(); - let root = cl::merkle::node(self.events_root(), root); - let root = cl::merkle::node(self.included_txs_root(), root); + let io_root = cl::merkle::node(self.events_root(), self.included_txs_root()); + + let balances_root = self.balances_root(); + let zone_id = self.zone_metadata.id(); + let state_root = cl::merkle::node(zone_id, balances_root); + + let root = cl::merkle::node(io_root, state_root); + StateCommitment(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 b7d7dc7..c4541bf 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 @@ -54,6 +54,8 @@ fn deposit( zone_funds_out, } = deposit; + let funds_vk = state.zone_metadata.funds_vk; + // 1) Check there are no more input/output notes than expected let inputs = [ deposit.commit().to_bytes().to_vec(), @@ -74,13 +76,13 @@ fn deposit( assert_eq!(ptx_root, pub_inputs.ptx_root); // 2) Check the deposit note is not already under control of the zone - assert_ne!(deposit.note.death_constraint, ZONE_FUNDS_VK); + assert_ne!(deposit.note.death_constraint, funds_vk); // 3) Check the ptx is balanced. This is not a requirement for standard ptxs, but we need it // in deposits (at least in a first version) to ensure fund tracking - assert_eq!(deposit.note.unit, *ZONE_UNIT); - assert_eq!(zone_funds_in.note.unit, *ZONE_UNIT); - assert_eq!(zone_funds_out.note.unit, *ZONE_UNIT); + assert_eq!(deposit.note.unit, *ZONE_CL_FUNDS_UNIT); + assert_eq!(zone_funds_in.note.unit, *ZONE_CL_FUNDS_UNIT); + assert_eq!(zone_funds_out.note.unit, *ZONE_CL_FUNDS_UNIT); let in_sum = deposit.note.value + zone_funds_in.note.value; @@ -89,8 +91,8 @@ fn deposit( assert_eq!(out_sum, in_sum, "deposit ptx is unbalanced"); // 4) Check the zone fund notes are correctly created - assert_eq!(zone_funds_in.note.death_constraint, ZONE_FUNDS_VK); - assert_eq!(zone_funds_out.note.death_constraint, ZONE_FUNDS_VK); + 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.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 @@ -136,6 +138,11 @@ fn validate_zone_input( let nf = Nullifier::new(input.input.nf_sk, input.input.nonce); assert_eq!(input.input.note.state, <[u8; 32]>::from(state.commit())); + // 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 + ); (ptx_root, nf) } @@ -149,10 +156,10 @@ 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, input.note.death_constraint); // the death constraint is the same as the on in the input + assert_eq!(output.note.death_constraint, state.zone_metadata.self_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, input.note.unit); // the balance unit 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 // the nonce is correctly evolved assert_eq!( From 584f81446523f719a398cd5091fc34f5a025cd7f Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Fri, 2 Aug 2024 15:06:40 +0200 Subject: [PATCH 2/5] check zone id in zone funds auth --- goas/atomic_asset_transfer/common/src/lib.rs | 10 ++++-- .../proof_statements/src/zone_funds.rs | 6 +++- .../risc0_proofs/spend_zone_funds/src/main.rs | 33 +++++++++++++++---- .../risc0_proofs/zone_state/src/main.rs | 6 ++-- 4 files changed, 43 insertions(+), 12 deletions(-) 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 From ca42d3ed4f1e8b2c3b5b5c42069c2f2d79dc75a8 Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Fri, 2 Aug 2024 17:43:53 +0200 Subject: [PATCH 3/5] simply merkle root calc --- goas/atomic_asset_transfer/common/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/goas/atomic_asset_transfer/common/src/lib.rs b/goas/atomic_asset_transfer/common/src/lib.rs index 852bdcf..2c9e323 100644 --- a/goas/atomic_asset_transfer/common/src/lib.rs +++ b/goas/atomic_asset_transfer/common/src/lib.rs @@ -59,11 +59,12 @@ impl StateWitness { pub fn commit(&self) -> StateCommitment { let io_root = cl::merkle::node(self.events_root(), self.included_txs_root()); - let balances_root = self.balances_root(); - let zone_id = self.zone_metadata.id(); - let state_root = cl::merkle::node(zone_id, balances_root); - - let root = cl::merkle::node(io_root, state_root); + let root = cl::merkle::root([ + self.events_root(), + self.included_txs_root(), + zone_id, + balances_root, + ]); StateCommitment(root) } From ca2c141d91f74b8a19e8d850c4f68e795b8aa204 Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Fri, 2 Aug 2024 17:44:03 +0200 Subject: [PATCH 4/5] use NullifierNonce::evolve --- .../risc0_proofs/spend_zone_funds/src/main.rs | 16 +++++++++------- .../risc0_proofs/zone_state/src/main.rs | 13 ++++++++----- 2 files changed, 17 insertions(+), 12 deletions(-) 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 9c24800..ab89247 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 @@ -2,11 +2,10 @@ /// /// Our goal: prove the zone authorized spending of funds use cl::merkle; -use cl::nullifier::{Nullifier, NullifierNonce, NullifierSecret}; +use cl::nullifier::{Nullifier, NullifierSecret}; use goas_proof_statements::zone_funds::SpendFundsPrivate; use proof_statements::death_constraint::DeathConstraintPublic; use risc0_zkvm::guest::env; -use sha2::{Digest, Sha256}; fn main() { let SpendFundsPrivate { @@ -41,10 +40,10 @@ fn main() { let spend_event_leaf = merkle::leaf(&spend_event.to_bytes()); 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!( + merkle::root([event_root, txs_root, zone_id, balances_root]), + zone_note.output.note.state + ); assert_eq!(ptx_root, out_zone_funds.ptx_root()); @@ -76,7 +75,10 @@ fn main() { ); assert_eq!( out_zone_funds.output.nonce, - NullifierNonce::from_bytes(Sha256::digest(&out_zone_funds.output.nonce.as_bytes()).into()) + in_zone_funds + .input + .nonce + .evolve(&NullifierSecret::from_bytes([0; 16])) ); // the state is propagated assert_eq!( 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 f1c6ef8..29063f8 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,7 +1,7 @@ use cl::{ input::InputWitness, merkle, - nullifier::{Nullifier, NullifierNonce, NullifierSecret}, + nullifier::{Nullifier, NullifierSecret}, partial_tx::{MAX_INPUTS, MAX_OUTPUTS}, PtxRoot, }; @@ -13,7 +13,6 @@ use proof_statements::{ ptx::{PartialTxInputPrivate, PartialTxOutputPrivate}, }; use risc0_zkvm::guest::env; -use sha2::{Digest, Sha256}; fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness { state.included_txs.push(Input::Withdraw(withdraw)); @@ -100,7 +99,9 @@ fn deposit( // nonce is correctly evolved assert_eq!( zone_funds_out.nonce, - NullifierNonce::from_bytes(Sha256::digest(&zone_funds_in.nonce.as_bytes()).into()) + zone_funds_in + .nonce + .evolve(&NullifierSecret::from_bytes([0; 16])) ); // 5) Check zone state notes are correctly created @@ -115,7 +116,9 @@ fn deposit( // nonce is correctly evolved assert_eq!( zone_note_out.nonce, - NullifierNonce::from_bytes(Sha256::digest(&zone_note_in.nonce.as_bytes()).into()) + zone_note_in + .nonce + .evolve(&NullifierSecret::from_bytes([0; 16])) ); let nullifier = Nullifier::new(zone_note_in.nf_sk, zone_note_in.nonce); assert_eq!(nullifier, pub_inputs.nf); @@ -166,7 +169,7 @@ fn validate_zone_output( // the nonce is correctly evolved assert_eq!( output.nonce, - NullifierNonce::from_bytes(Sha256::digest(&input.nonce.as_bytes()).into()) + input.nonce.evolve(&NullifierSecret::from_bytes([0; 16])) ); } From 5b03e070b5f24e10db17985082378f3b34dd90b1 Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Fri, 2 Aug 2024 17:57:19 +0200 Subject: [PATCH 5/5] Add evolved nonce --- goas/atomic_asset_transfer/common/src/lib.rs | 6 ++---- .../risc0_proofs/spend_zone_funds/src/main.rs | 5 +---- .../risc0_proofs/zone_state/src/main.rs | 19 +++---------------- goas/cl/cl/src/input.rs | 18 +++++++++++------- 4 files changed, 17 insertions(+), 31 deletions(-) diff --git a/goas/atomic_asset_transfer/common/src/lib.rs b/goas/atomic_asset_transfer/common/src/lib.rs index 2c9e323..dbc18c5 100644 --- a/goas/atomic_asset_transfer/common/src/lib.rs +++ b/goas/atomic_asset_transfer/common/src/lib.rs @@ -57,13 +57,11 @@ impl StateWitness { /// / \ / \ /// events txs zoneid balances pub fn commit(&self) -> StateCommitment { - let io_root = cl::merkle::node(self.events_root(), self.included_txs_root()); - let root = cl::merkle::root([ self.events_root(), self.included_txs_root(), - zone_id, - balances_root, + self.zone_metadata.id(), + self.balances_root(), ]); StateCommitment(root) 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 ab89247..d4b59de 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 @@ -75,10 +75,7 @@ fn main() { ); assert_eq!( out_zone_funds.output.nonce, - in_zone_funds - .input - .nonce - .evolve(&NullifierSecret::from_bytes([0; 16])) + in_zone_funds.input.evolved_nonce() ); // the state is propagated assert_eq!( 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 29063f8..50216c6 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 @@ -97,12 +97,7 @@ fn deposit( 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 - assert_eq!( - zone_funds_out.nonce, - zone_funds_in - .nonce - .evolve(&NullifierSecret::from_bytes([0; 16])) - ); + assert_eq!(zone_funds_out.nonce, zone_funds_in.evolved_nonce()); // 5) Check zone state notes are correctly created assert_eq!( @@ -114,12 +109,7 @@ fn deposit( assert_eq!(zone_note_in.note.unit, zone_note_out.note.unit); assert_eq!(zone_note_in.note.value, zone_note_out.note.value); // nonce is correctly evolved - assert_eq!( - zone_note_out.nonce, - zone_note_in - .nonce - .evolve(&NullifierSecret::from_bytes([0; 16])) - ); + assert_eq!(zone_note_out.nonce, zone_note_in.evolved_nonce()); let nullifier = Nullifier::new(zone_note_in.nf_sk, zone_note_in.nonce); assert_eq!(nullifier, pub_inputs.nf); @@ -167,10 +157,7 @@ fn validate_zone_output( assert_eq!(output.note.unit, state.zone_metadata.unit); // the balance unit is the same as in the input // the nonce is correctly evolved - assert_eq!( - output.nonce, - input.nonce.evolve(&NullifierSecret::from_bytes([0; 16])) - ); + assert_eq!(output.nonce, input.evolved_nonce()); } fn main() { diff --git a/goas/cl/cl/src/input.rs b/goas/cl/cl/src/input.rs index 17b495c..af056a2 100644 --- a/goas/cl/cl/src/input.rs +++ b/goas/cl/cl/src/input.rs @@ -41,13 +41,17 @@ impl InputWitness { } } + pub fn evolved_nonce(&self) -> NullifierNonce { + self.nonce.evolve(&self.nf_sk) + } + pub fn evolve_output(&self, balance_blinding: BalanceWitness) -> crate::OutputWitness { - crate::OutputWitness { - note: self.note, - balance_blinding, - nf_pk: self.nf_sk.commit(), - nonce: self.nonce.evolve(&self.nf_sk), - } + crate::OutputWitness { + note: self.note, + balance_blinding, + nf_pk: self.nf_sk.commit(), + nonce: self.evolved_nonce(), + } } pub fn nullifier(&self) -> Nullifier { @@ -63,7 +67,7 @@ impl InputWitness { } pub fn note_commitment(&self) -> crate::NoteCommitment { - self.note.commit(self.nf_sk.commit(), self.nonce) + self.note.commit(self.nf_sk.commit(), self.nonce) } }