diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index e68afdd7bf8..bde4e4fefab 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -23,14 +23,15 @@ struct AddressNote { impl NoteInterface for AddressNote { - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index 3d9c408afd7..eae2bd8a76e 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -66,7 +66,9 @@ mod test { fn set_header(&mut self, header: NoteHeader) {self.header = header; } - fn compute_nullifier(self, context: &mut PrivateContext) -> Field {1} + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { + [1, 1] + } fn compute_nullifier_without_context(self) -> Field {1} diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index 2be46b95500..fe0e222369f 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -57,9 +57,12 @@ pub fn destroy_note( context: &mut PrivateContext, note: Note ) where Note: NoteInterface { - let mut nullifier = 0; let mut consumed_note_hash: Field = 0; - nullifier = note.compute_nullifier(context); + // TODO(benesjan): try just directly setting the value to consumed_note_hash and not doing the if-else down + // let [note_hash, nullifier] = note.compute_note_hash_and_nullifier(context); + let result = note.compute_note_hash_and_nullifier(context); + let note_hash = result[0]; + let nullifier = result[1]; // We also need the note hash corresponding to the "nullifier" let header = note.get_header(); @@ -67,11 +70,10 @@ pub fn destroy_note( // A non-zero note hash counter implies that we're nullifying a transient note (i.e. one that has not yet been // persisted in the trees and is instead in the pending new note hashes array). In such a case we compute its hash // to inform the kernel which note we're nullifyng so that it can find it and squash both the note and the - // nullifier. This value is unused when nullifying non transient notes - in that case the kernel simply persists + // nullifier. This value is unused when nullifying non-transient notes - in that case the kernel simply persists // the nullifier in the tree. if (header.note_hash_counter != 0) { - // TODO(1718): Can we reuse the note hash computed in `compute_nullifier`? - consumed_note_hash = compute_note_hash_for_consumption(note); + consumed_note_hash = note_hash; } let nullifier_counter = context.side_effect_counter; diff --git a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr index c9749de15f8..904b0236360 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -4,7 +4,7 @@ use dep::protocol_types::grumpkin_point::GrumpkinPoint; // docs:start:note_interface trait NoteInterface { - fn compute_nullifier(self, context: &mut PrivateContext) -> Field; + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2]; fn compute_nullifier_without_context(self) -> Field; diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index cc98cdf96a5..6b0b1c5e799 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -57,7 +57,7 @@ pub fn compute_siloed_nullifier( context: &mut PrivateContext ) -> Field where Note: NoteInterface { let header = note_with_header.get_header(); - let inner_nullifier = note_with_header.compute_nullifier(context); + let inner_nullifier = note_with_header.compute_note_hash_and_nullifier(context)[1]; let input = [header.contract_address.to_field(), inner_nullifier]; pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr index 1a66e07692c..34ead0f136b 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr @@ -36,8 +36,8 @@ impl PrivateMutable { // Under such circumstances, such application developers might wish to _not_ use this state variable type. // This is especially dangerous for initial assignment to elements of a `Map` type (for example), because the storage slot often also identifies an actor. e.g. // the initial assignment to `my_map.at(msg.sender)` will leak: `msg.sender`, the fact that an element of `my_map` was assigned-to for the first time, and the contract_address. - // Note: subsequent nullification of this state variable, via the `replace` method will not be leaky, if the `compute_nullifier()` method of the underlying note is designed to ensure privacy. - // For example, if the `compute_nullifier()` method injects the secret key of a note owner into the computed nullifier's preimage. + // Note: subsequent nullification of this state variable, via the `replace` method will not be leaky, if the `compute_note_hash_and_nullifier()` method of the underlying note is designed to ensure privacy. + // For example, if the `compute_note_hash_and_nullifier()` method injects the secret key of a note owner into the computed nullifier's preimage. pub fn compute_initialization_nullifier(self) -> Field { pedersen_hash( [self.storage_slot], diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr index 8a7b853c794..cdbd196eb63 100644 --- a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr +++ b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr @@ -39,8 +39,8 @@ impl NoteInterface for MockNote { 0 } - fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { - 0 + fn compute_note_hash_and_nullifier(self, _context: &mut PrivateContext) -> [Field; 2] { + [0, 0] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index d0bf0c65d04..6cdacc151c1 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -24,14 +24,15 @@ struct ValueNote { impl NoteInterface for ValueNote { // docs:start:nullifier - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } // docs:end:nullifier diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 389cf3278d5..c4247574f3f 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -19,14 +19,15 @@ struct SubscriptionNote { } impl NoteInterface for SubscriptionNote { - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr b/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr index 7a0b7a15ef0..cc00d1ac16a 100644 --- a/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/claim_contract/src/main.nr @@ -38,7 +38,8 @@ contract Claim { // Note: Only the owner of the npk_m will be able to produce the nsk_app and compute this nullifier. // The nullifier is unique to the note and THIS contract because the protocol siloes all nullifiers with // the address of a contract it was emitted from. - context.push_new_nullifier(proof_note.compute_nullifier(&mut context), 0); + let nullifier = proof_note.compute_note_hash_and_nullifier(&mut context)[1]; + context.push_new_nullifier(nullifier, 0); // 4) Finally we mint the reward token to the sender of the transaction Token::at(storage.reward_token.read_private()).mint_public(recipient, proof_note.value).enqueue(&mut context); diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index b51eaecc668..7d060488f06 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -30,14 +30,15 @@ impl CardNote { } impl NoteInterface for CardNote { - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index 0bc2689891e..9c27e4c7245 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -65,14 +65,15 @@ impl NoteInterface f EcdsaPublicKeyNote { x, y, npk_m_hash: serialized_note[4], header: NoteHeader::empty() } } - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index fdc55f5e363..36a7bdaa2f9 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -19,14 +19,15 @@ struct PublicKeyNote { } impl NoteInterface for PublicKeyNote { - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr b/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr index bcca5adbab6..aadc5ee6193 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr @@ -18,9 +18,9 @@ struct TestNote { impl NoteInterface for TestNote { - fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, _context: &mut PrivateContext) -> [Field; 2] { // This note is expected to be shared between users and for this reason can't be nullified using a secret. - 0 + [0, 0] } fn compute_nullifier_without_context(self) -> Field { diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 0a65ce2d54e..0b1571bbd19 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -8,7 +8,6 @@ use dep::aztec::{ trait OwnedNote { fn new(amount: U128, owner_npk_m_hash: Field) -> Self; fn get_amount(self) -> U128; - fn get_owner_npk_m_hash(self) -> Field; } global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. @@ -27,14 +26,15 @@ struct TokenNote { impl NoteInterface for TokenNote { // docs:start:nullifier - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } // docs:end:nullifier @@ -62,8 +62,4 @@ impl OwnedNote for TokenNote { fn get_amount(self) -> U128 { self.amount } - - fn get_owner_npk_m_hash(self) -> Field { - self.npk_m_hash - } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr index c93b9b18956..ed264101f66 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr @@ -41,8 +41,9 @@ impl NoteInterface for Transpa } // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): Ensure nullifier collisions are prevented - fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { - self.compute_nullifier_without_context() + fn compute_note_hash_and_nullifier(self, _context: &mut PrivateContext) -> [Field; 2] { + // TODO(benesjan): Can we really return 0 for note hash here? Try just asser(false) here. + [0, self.compute_nullifier_without_context()] } // Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index 2a797ec83e8..0b1571bbd19 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_point::GrumpkinPoint, hash::poseidon2_hash}, note::utils::compute_note_hash_for_consumption, oracle::unsafe_rand::unsafe_rand, - note::note_emission::NoteEmission, keys::getters::get_nsk_app + keys::getters::get_nsk_app }; trait OwnedNote { @@ -26,14 +26,15 @@ struct TokenNote { impl NoteInterface for TokenNote { // docs:start:nullifier - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> [Field; 2] { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash([ + let nullifier = poseidon2_hash([ note_hash_for_nullify, secret, GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ]) + ]); + [note_hash_for_nullify, nullifier] } // docs:end:nullifier diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr index c93b9b18956..a253ec8180a 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr @@ -41,8 +41,9 @@ impl NoteInterface for Transpa } // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): Ensure nullifier collisions are prevented - fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { - self.compute_nullifier_without_context() + fn compute_note_hash_and_nullifier(self, _context: &mut PrivateContext) -> [Field; 2] { + // TODO(benesjan): Can we really return 0 for note hash here? + [0, self.compute_nullifier_without_context()] } // Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as