diff --git a/boxes/blank/src/contracts/src/main.nr b/boxes/blank/src/contracts/src/main.nr index c5ccfa0ff44..d544c909a97 100644 --- a/boxes/blank/src/contracts/src/main.nr +++ b/boxes/blank/src/contracts/src/main.nr @@ -23,6 +23,7 @@ contract Blank { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/boxes/token/src/contracts/Nargo.toml b/boxes/token/src/contracts/Nargo.toml index f5e1cc74cf6..e229ed0cd72 100644 --- a/boxes/token/src/contracts/Nargo.toml +++ b/boxes/token/src/contracts/Nargo.toml @@ -5,7 +5,7 @@ compiler_version = ">=0.18.0" type = "contract" [dependencies] -value_note = { path = "../../../../yarn-project/aztec-nr/value-note"} +aztec = { path = "../../../../yarn-project/aztec-nr/aztec" } safe_math = { path = "../../../../yarn-project/aztec-nr/safe-math" } +compressed_string = {path = "../../../../yarn-project/aztec-nr/compressed-string"} authwit = { path = "../../../../yarn-project/aztec-nr/authwit" } -aztec = { path = "../../../../yarn-project/aztec-nr/aztec" } diff --git a/boxes/token/src/contracts/src/main.nr b/boxes/token/src/contracts/src/main.nr index 6bef8316bea..f48b637ef6d 100644 --- a/boxes/token/src/contracts/src/main.nr +++ b/boxes/token/src/contracts/src/main.nr @@ -14,6 +14,7 @@ contract Token { use dep::std::option::Option; use dep::safe_math::SafeU120; + use dep::compressed_string::FieldCompressedString; use dep::aztec::{ note::{ @@ -22,10 +23,10 @@ contract Token { utils as note_utils, }, hash::{compute_secret_hash}, - state_vars::{map::Map, public_state::PublicState, set::Set}, + state_vars::{map::Map, public_state::PublicState, stable_public_state::StablePublicState, set::Set}, protocol_types::{ abis::function_selector::FunctionSelector, - address::AztecAddress, + address::AztecAddress } }; @@ -41,7 +42,7 @@ contract Token { use crate::types::{ transparent_note::TransparentNote, token_note::{TokenNote, TOKEN_NOTE_LEN}, - balances_map::{BalancesMap}, + balances_map::BalancesMap }; // docs:end::imports @@ -51,24 +52,35 @@ contract Token { admin: PublicState, // docs:end:storage_admin // docs:start:storage_minters - minters: Map>, + minters: Map>, // docs:end:storage_minters // docs:start:storage_balances balances: BalancesMap, // docs:end:storage_balances - total_supply: PublicState, + total_supply: PublicState, // docs:start:storage_pending_shields - pending_shields: Set, + pending_shields: Set, // docs:end:storage_pending_shields public_balances: Map>, + symbol: StablePublicState, + name: StablePublicState, + // docs:start:storage_decimals + decimals: StablePublicState, + // docs:end:storage_decimals } // docs:end:storage_struct // docs:start:constructor #[aztec(private)] - fn constructor(admin: AztecAddress) { - let selector = FunctionSelector::from_signature("_initialize((Field))"); - context.call_public_function(context.this_address(), selector, [admin.to_field()]); + fn constructor(admin: AztecAddress, name: str<31>, symbol: str<31>, decimals: u8) { + let selector = FunctionSelector::from_signature("_initialize((Field),(Field),(Field),u8)"); + let name_s = FieldCompressedString::from_string(name); + let symbol_s = FieldCompressedString::from_string(symbol); + context.call_public_function( + context.this_address(), + selector, + [admin.to_field(), name_s.serialize()[0], symbol_s.serialize()[0], decimals as Field] + ); } // docs:end:constructor @@ -82,6 +94,52 @@ contract Token { } // docs:end:set_admin + #[aztec(public)] + fn public_get_name() -> pub FieldCompressedString { + storage.name.read_public() + } + + #[aztec(private)] + fn private_get_name() -> pub FieldCompressedString { + storage.name.read_private() + } + + unconstrained fn un_get_name() -> pub [u8; 31] { + storage.name.read_public().to_bytes() + } + + #[aztec(public)] + fn public_get_symbol() -> pub FieldCompressedString { + storage.symbol.read_public() + } + + #[aztec(private)] + fn private_get_symbol() -> pub FieldCompressedString { + storage.symbol.read_private() + } + + unconstrained fn un_get_symbol() -> pub [u8; 31] { + storage.symbol.read_public().to_bytes() + } + + #[aztec(public)] + fn public_get_decimals() -> pub u8 { + // docs:start:read_decimals_public + storage.decimals.read_public() + // docs:end:read_decimals_public + } + + #[aztec(private)] + fn private_get_decimals() -> pub u8 { + // docs:start:read_decimals_private + storage.decimals.read_private() + // docs:end:read_decimals_private + } + + unconstrained fn un_get_decimals() -> pub u8 { + storage.decimals.read_public() + } + // docs:start:set_minter #[aztec(public)] fn set_minter(minter: AztecAddress, approve: bool) { @@ -188,12 +246,12 @@ contract Token { fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) { let pending_shields = storage.pending_shields; let secret_hash = compute_secret_hash(secret); - // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount, Option::none())) and secret_hash - // stored in field with index 1 (select(1, secret_hash, Option::none())). + // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash + // stored in field with index 1 (select(1, secret_hash)). let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, secret_hash, Option::none()).set_limit(1); let notes = pending_shields.get_notes(options); let note = notes[0].unwrap_unchecked(); - // Remove the note from the pending shields set + // Remove the note from the pending shields set pending_shields.remove(note); // Add the token note to user's balances set @@ -254,10 +312,20 @@ contract Token { // docs:start:initialize #[aztec(public)] - internal fn _initialize(new_admin: AztecAddress) { + internal fn _initialize( + new_admin: AztecAddress, + name: FieldCompressedString, + symbol: FieldCompressedString, + decimals: u8 + ) { assert(!new_admin.is_zero(), "invalid admin"); storage.admin.write(new_admin); storage.minters.at(new_admin).write(true); + storage.name.initialize(name); + storage.symbol.initialize(symbol); + // docs:start:initialize_decimals + storage.decimals.initialize(decimals); + // docs:end:initialize_decimals } // docs:end:initialize @@ -312,9 +380,6 @@ contract Token { } // docs:end:balance_of_public - // Below this point is the stuff of nightmares. - // This should ideally not be required. What do we do if vastly different types of serialized_notes? - // docs:start:compute_note_hash_and_nullifier // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. @@ -323,10 +388,12 @@ contract Token { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; TOKEN_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - if (storage_slot == storage.pending_shields.get_storage_slot()) { + + if (note_type_id == TransparentNote::get_note_type_id()) { note_utils::compute_note_hash_and_nullifier( TransparentNote::deserialize_content, note_header, diff --git a/boxes/token/src/contracts/src/types/balances_map.nr b/boxes/token/src/contracts/src/types/balances_map.nr index 286a7825645..8452ba327a7 100644 --- a/boxes/token/src/contracts/src/types/balances_map.nr +++ b/boxes/token/src/contracts/src/types/balances_map.nr @@ -2,7 +2,6 @@ use dep::std::option::Option; use dep::safe_math::SafeU120; use dep::aztec::{ context::Context, - hash::pedersen_hash, protocol_types::{ address::AztecAddress, constants::MAX_READ_REQUESTS_PER_CALL, diff --git a/boxes/token/src/contracts/src/types/token_note.nr b/boxes/token/src/contracts/src/types/token_note.nr index 7c11144e1fb..7dfe681f395 100644 --- a/boxes/token/src/contracts/src/types/token_note.nr +++ b/boxes/token/src/contracts/src/types/token_note.nr @@ -103,11 +103,18 @@ impl NoteInterface for TokenNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } - } + } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'TokenNote')))" + 8411110710111078111116101 + } } impl OwnedNote for TokenNote { diff --git a/boxes/token/src/contracts/src/types/transparent_note.nr b/boxes/token/src/contracts/src/types/transparent_note.nr index 8754cf2f889..6ac87c783fb 100644 --- a/boxes/token/src/contracts/src/types/transparent_note.nr +++ b/boxes/token/src/contracts/src/types/transparent_note.nr @@ -62,6 +62,12 @@ impl NoteInterface for TransparentNote { fn broadcast(self, context: &mut PrivateContext, slot: Field) { assert(false, "TransparentNote does not support broadcast"); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'TransparentNote')))" + 84114971101151129711410111011678111116101 + } } impl TransparentNote { diff --git a/boxes/token/src/tests/token.contract.test.ts b/boxes/token/src/tests/token.contract.test.ts index 5fbcfcd4179..ae7acbea3aa 100644 --- a/boxes/token/src/tests/token.contract.test.ts +++ b/boxes/token/src/tests/token.contract.test.ts @@ -21,6 +21,9 @@ import { afterEach, beforeAll, expect, jest } from '@jest/globals'; import { setupEnvironment } from '../environment/index.js'; const TIMEOUT = 60_000; +const TOKEN_NAME = 'Aztec Token'; +const TOKEN_SYMBOL = 'AZT'; +const TOKEN_DECIMALS = 18n; describe('e2e_token_contract', () => { jest.setTimeout(TIMEOUT); @@ -36,8 +39,16 @@ describe('e2e_token_contract', () => { const addPendingShieldNoteToPXE = async (accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) => { const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote(note, accounts[accountIndex].address, asset.address, storageSlot, txHash); + const extendedNote = new ExtendedNote( + note, + accounts[accountIndex].address, + asset.address, + storageSlot, + noteTypeId, + txHash, + ); await wallets[accountIndex].addNote(extendedNote); }; @@ -51,7 +62,9 @@ describe('e2e_token_contract', () => { logger(`Accounts: ${accounts.map(a => a.toReadableString())}`); logger(`Wallets: ${wallets.map(w => w.getAddress().toString())}`); - asset = await TokenContract.deploy(wallets[0], accounts[0].address).send().deployed(); + asset = await TokenContract.deploy(wallets[0], accounts[0], TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS) + .send() + .deployed(); logger(`Token deployed to ${asset.address}`); tokenSim = new TokenSimulator( asset, diff --git a/docs/docs/developers/getting_started/aztecnr-getting-started.md b/docs/docs/developers/getting_started/aztecnr-getting-started.md index 0bd535ec764..ab717925cdd 100644 --- a/docs/docs/developers/getting_started/aztecnr-getting-started.md +++ b/docs/docs/developers/getting_started/aztecnr-getting-started.md @@ -120,7 +120,7 @@ Add a new function into your contract as shown below: #include_code nullifier /yarn-project/noir-contracts/contracts/counter_contract/src/main.nr rust -Here, we're computing both the note hash and the nullifier. The nullifier computation uses Aztec’s `compute_note_hash_and_nullifier` function, which takes details about the note's attributes eg contract address, nonce, storage slot, and preimage. +Here, we're computing both the note hash and the nullifier. The nullifier computation uses Aztec’s `compute_note_hash_and_nullifier` function, which takes details about the note's attributes eg contract address, nonce, storage slot, type id, and preimage. ## Getting a counter diff --git a/docs/docs/developers/tutorials/writing_token_contract.md b/docs/docs/developers/tutorials/writing_token_contract.md index fe35268ac32..6002d717b61 100644 --- a/docs/docs/developers/tutorials/writing_token_contract.md +++ b/docs/docs/developers/tutorials/writing_token_contract.md @@ -130,7 +130,7 @@ contract Token { unconstrained fn balance_of_public(owner: AztecAddress) -> Field {} - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {} + unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {} } ``` diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 035a838141b..cdee45c4ecf 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,12 +6,74 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## TBD + +### Introduce Note Type IDs + +Note Type IDs are a new feature which enable contracts to have multiple `Map`s with different underlying note types, something that was not possible before. This is done almost without any user intervention, though some minor changes are required. + +The mandatory `compute_note_hash_and_nullifier` now has a fifth parameter `note_type_id`. Use this instead of `storage_slot` to determine which deserialization function to use. + +Before: + +```rust +unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + preimage: [Field; TOKEN_NOTE_LEN] +) -> pub [Field; 4] { + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + + if (storage_slot == storage.pending_shields.get_storage_slot()) { + note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize_content, note_header, preimage) + } else if (note_type_id == storage.slow_update.get_storage_slot()) { + note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, preimage) + } else { + note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, preimage) + } +``` + +Now: + +```rust +unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + preimage: [Field; TOKEN_NOTE_LEN] +) -> pub [Field; 4] { + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + + if (note_type_id == TransparentNote::get_note_type_id()) { + note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize_content, note_header, preimage) + } else if (note_type_id == FieldNote::get_note_type_id()) { + note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, preimage) + } else { + note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, preimage) + } +``` + +The `NoteInterface` trait now has an additional `get_note_type_id()` function. This implementation will be autogenerated in the future, but for now providing any unique ID will suffice. The suggested way to do it is by running the Python command shown in the comment below: + +```rust +impl NoteInterface for MyCustomNote { + fn get_note_type_id() -> Field { + // python -c "print(int(''.join(str(ord(c)) for c in 'MyCustomNote')))" + 771216711711511611110978111116101 + } +} +``` + ## 0.22.0 ### `Note::compute_note_hash` renamed to `Note::compute_note_content_hash` + The `compute_note_hash` function in of the `Note` trait has been renamed to `compute_note_content_hash` to avoid being confused with the actual note hash. Before: + ```rust impl NoteInterface for CardNote { fn compute_note_hash(self) -> Field { @@ -22,6 +84,7 @@ impl NoteInterface for CardNote { ``` Now: + ```rust impl NoteInterface for CardNote { fn compute_note_content_hash(self) -> Field { @@ -43,6 +106,7 @@ Makes a split in logic for note hash computation for consumption and insertion. The `NoteInterface` have been extended to include `serialize_content` and `deserialize_content` functions. This is to convey the difference between serializing the full note, and just the content. This change allows you to also add a `serialize` function to support passing in a complete note to a function. Before: + ```rust impl Serialize for AddressNote { fn serialize(self) -> [Field; ADDRESS_NOTE_LEN]{ @@ -60,7 +124,8 @@ impl Deserialize for AddressNote { } ``` -Now +Now + ```rust impl NoteInterface for AddressNote { fn serialize_content(self) -> [Field; ADDRESS_NOTE_LEN]{ @@ -83,7 +148,7 @@ impl NoteInterface for AddressNote { Storage definition and initialization has been simplified. Previously: -```rust +```rust struct Storage { leader: PublicState, legendary_card: Singleton, @@ -115,9 +180,9 @@ impl Storage { } ``` -Now: +Now: -```rust +```rust struct Storage { leader: PublicState, legendary_card: Singleton, @@ -255,9 +320,9 @@ global CardNoteMethods = NoteInterface { }; ``` -Now: +Now: -```rust +```rust use dep::aztec::{ note::{ note_header::NoteHeader, @@ -352,7 +417,7 @@ impl NoteInterface for CardNote { } ``` -Public state must implement Serialize and Deserialize traits. +Public state must implement Serialize and Deserialize traits. It is still possible to manually implement the storage initialization (for custom storage wrappers or internal types that don't implement the required traits). For the above example, the `impl Storage` section would look like this: diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 7ca4b79eed8..0f054c262bf 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -337,26 +337,27 @@ fn check_for_storage_implementation(module: &SortedModule) -> bool { }) } -// Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,[Field; N]) -> [Field; 4]" is defined +// Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) -> bool { module.functions.iter().any(|func| { func.def.name.0.contents == "compute_note_hash_and_nullifier" - && func.def.parameters.len() == 4 + && func.def.parameters.len() == 5 && match &func.def.parameters[0].typ.typ { UnresolvedTypeData::Named(path, _, _) => path.segments.last().unwrap().0.contents == "AztecAddress", _ => false, } && func.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement && func.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement - // checks if the 4th parameter is an array and the Box in + && func.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement + // checks if the 5th parameter is an array and the Box in // Array(Option, Box) contains only fields - && match &func.def.parameters[3].typ.typ { + && match &func.def.parameters[4].typ.typ { UnresolvedTypeData::Array(_, inner_type) => { matches!(inner_type.typ, UnresolvedTypeData::FieldElement) }, _ => false, } - // We check the return type the same way as we did the 4th parameter + // We check the return type the same way as we did the 5th parameter && match &func.def.return_type { FunctionReturnType::Default(_) => false, FunctionReturnType::Ty(unresolved_type) => { diff --git a/noir/tooling/nargo_fmt/tests/expected/contract.nr b/noir/tooling/nargo_fmt/tests/expected/contract.nr index ed828289d22..b80efeeb692 100644 --- a/noir/tooling/nargo_fmt/tests/expected/contract.nr +++ b/noir/tooling/nargo_fmt/tests/expected/contract.nr @@ -79,6 +79,7 @@ contract Benchmarking { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, preimage: [Field; VALUE_NOTE_LEN] ) -> [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/noir/tooling/nargo_fmt/tests/input/contract.nr b/noir/tooling/nargo_fmt/tests/input/contract.nr index 2e3f4d7c8c4..d10bfb745b6 100644 --- a/noir/tooling/nargo_fmt/tests/input/contract.nr +++ b/noir/tooling/nargo_fmt/tests/input/contract.nr @@ -75,6 +75,7 @@ contract Benchmarking { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, preimage: [Field; VALUE_NOTE_LEN] ) -> [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/aztec-nr/address-note/src/address_note.nr b/yarn-project/aztec-nr/address-note/src/address_note.nr index 2ce95fe79d6..ebacf94a631 100644 --- a/yarn-project/aztec-nr/address-note/src/address_note.nr +++ b/yarn-project/aztec-nr/address-note/src/address_note.nr @@ -76,11 +76,18 @@ impl NoteInterface for AddressNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); // docs:end:encrypted } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'AddressNote')))" + 6510010011410111511578111116101 + } } impl AddressNote { diff --git a/yarn-project/aztec-nr/aztec/src/log.nr b/yarn-project/aztec-nr/aztec/src/log.nr index b70c199f5aa..033db50e09d 100644 --- a/yarn-project/aztec-nr/aztec/src/log.nr +++ b/yarn-project/aztec-nr/aztec/src/log.nr @@ -6,10 +6,17 @@ pub fn emit_encrypted_log( context: &mut PrivateContext, contract_address: AztecAddress, storage_slot: Field, + note_type_id: Field, encryption_pub_key: GrumpkinPoint, log: [Field; N] ) { - let _ = oracle::logs::emit_encrypted_log(contract_address, storage_slot, encryption_pub_key, log); + let _ = oracle::logs::emit_encrypted_log( + contract_address, + storage_slot, + note_type_id, + encryption_pub_key, + log + ); context.accumulate_encrypted_logs(log); } diff --git a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr index 0596ac66ea1..dfad659a717 100644 --- a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr +++ b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr @@ -21,7 +21,15 @@ pub fn create_note( // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 let serialized_note: [Field; N] = Note::serialize_content(*note); - assert(notify_created_note(storage_slot, serialized_note, inner_note_hash) == 0); + assert( + notify_created_note( + storage_slot, + Note::get_note_type_id(), + serialized_note, + inner_note_hash + ) + == 0 + ); context.push_new_note_hash(inner_note_hash); diff --git a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr index cfe9ea769ed..a663051fe1e 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr @@ -18,6 +18,8 @@ trait NoteInterface { fn compute_nullifier_without_context(self) -> Field; fn broadcast(self, context: &mut PrivateContext, slot: Field) -> (); + + fn get_note_type_id() -> Field; } // docs:end:note_interface diff --git a/yarn-project/aztec-nr/aztec/src/note/note_type_id.nr b/yarn-project/aztec-nr/aztec/src/note/note_type_id.nr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/yarn-project/aztec-nr/aztec/src/oracle/logs.nr b/yarn-project/aztec-nr/aztec/src/oracle/logs.nr index 04979ab54c8..1bbede44c45 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/logs.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/logs.nr @@ -5,6 +5,7 @@ use dep::protocol_types::{address::AztecAddress, constants::NUM_FIELDS_PER_SHA25 fn emit_encrypted_log_oracle( _contract_address: AztecAddress, _storage_slot: Field, + _note_type_id: Field, _encryption_pub_key: GrumpkinPoint, _preimage: [Field; N] ) -> Field {} @@ -12,10 +13,19 @@ fn emit_encrypted_log_oracle( unconstrained pub fn emit_encrypted_log( contract_address: AztecAddress, storage_slot: Field, + note_type_id: Field, encryption_pub_key: GrumpkinPoint, preimage: [Field; N] ) -> [Field; NUM_FIELDS_PER_SHA256] { - [emit_encrypted_log_oracle(contract_address, storage_slot, encryption_pub_key, preimage), 0] + [ + emit_encrypted_log_oracle( + contract_address, + storage_slot, + note_type_id, + encryption_pub_key, + preimage + ), 0 + ] } #[oracle(emitUnencryptedLog)] diff --git a/yarn-project/aztec-nr/aztec/src/oracle/notes.nr b/yarn-project/aztec-nr/aztec/src/oracle/notes.nr index f6378c1ecd6..42b2f356ec8 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/notes.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/notes.nr @@ -10,10 +10,20 @@ use dep::protocol_types::{ }; #[oracle(notifyCreatedNote)] -fn notify_created_note_oracle(_storage_slot: Field, _serialized_note: [Field; N], _inner_note_hash: Field) -> Field {} +fn notify_created_note_oracle( + _storage_slot: Field, + _note_type_id: Field, + _serialized_note: [Field; N], + _inner_note_hash: Field +) -> Field {} -unconstrained pub fn notify_created_note(storage_slot: Field, serialized_note: [Field; N], inner_note_hash: Field) -> Field { - notify_created_note_oracle(storage_slot, serialized_note, inner_note_hash) +unconstrained pub fn notify_created_note( + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; N], + inner_note_hash: Field +) -> Field { + notify_created_note_oracle(storage_slot, note_type_id, serialized_note, inner_note_hash) } #[oracle(notifyNullifiedNote)] diff --git a/yarn-project/aztec-nr/field-note/src/field_note.nr b/yarn-project/aztec-nr/field-note/src/field_note.nr index 439b4ad6378..1f54659636a 100644 --- a/yarn-project/aztec-nr/field-note/src/field_note.nr +++ b/yarn-project/aztec-nr/field-note/src/field_note.nr @@ -53,6 +53,12 @@ impl NoteInterface for FieldNote { false, "FieldNote does not support broadcast. Add it to PXE directly using the `.addNote` function." ); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'FieldNote')))" + 7010510110810078111116101 + } } impl FieldNote { diff --git a/yarn-project/aztec-nr/value-note/src/value_note.nr b/yarn-project/aztec-nr/value-note/src/value_note.nr index 999197af9a6..f29769ee3a3 100644 --- a/yarn-project/aztec-nr/value-note/src/value_note.nr +++ b/yarn-project/aztec-nr/value-note/src/value_note.nr @@ -76,10 +76,17 @@ impl NoteInterface for ValueNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'ValueNote')))" + 869710811710178111116101 + } } impl ValueNote { diff --git a/yarn-project/aztec/src/examples/token.ts b/yarn-project/aztec/src/examples/token.ts index f5f62c73e56..e741df3d442 100644 --- a/yarn-project/aztec/src/examples/token.ts +++ b/yarn-project/aztec/src/examples/token.ts @@ -50,8 +50,17 @@ async function main() { // Add the newly created "pending shield" note to PXE const pendingShieldsStorageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. + // `pending_shields` underlying note type is TransparentNote, with the following type id. + const pendingShieldsNoteTypeId = new Fr(84114971101151129711410111011678111116101n); const note = new Note([new Fr(ALICE_MINT_BALANCE), aliceSecretHash]); - const extendedNote = new ExtendedNote(note, alice.address, token.address, pendingShieldsStorageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + alice.address, + token.address, + pendingShieldsStorageSlot, + pendingShieldsNoteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); // Make the tokens spendable by redeeming them using the secret (converts the "pending shield note" created above diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts index c233e2ba7bd..1dd11c02cee 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts @@ -25,6 +25,10 @@ export class L1NotePayload { * Storage slot of the contract this tx is interacting with. */ public storageSlot: Fr, + /** + * Type identifier for the underlying note, required to determine how to compute its hash and nullifier. + */ + public noteTypeId: Fr, ) {} /** @@ -34,7 +38,12 @@ export class L1NotePayload { */ static fromBuffer(buffer: Buffer | BufferReader): L1NotePayload { const reader = BufferReader.asReader(buffer); - return new L1NotePayload(reader.readObject(Note), reader.readObject(AztecAddress), Fr.fromBuffer(reader)); + return new L1NotePayload( + reader.readObject(Note), + reader.readObject(AztecAddress), + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), + ); } /** @@ -42,7 +51,7 @@ export class L1NotePayload { * @returns Buffer representation of the L1NotePayload object. */ toBuffer() { - return serializeToBuffer([this.note, this.contractAddress, this.storageSlot]); + return serializeToBuffer([this.note, this.contractAddress, this.storageSlot, this.noteTypeId]); } /** @@ -80,6 +89,6 @@ export class L1NotePayload { * @returns A random L1NotePayload object. */ static random() { - return new L1NotePayload(Note.random(), AztecAddress.random(), Fr.random()); + return new L1NotePayload(Note.random(), AztecAddress.random(), Fr.random(), Fr.random()); } } diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index e6e8a2489a9..30da4aa900c 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -63,6 +63,7 @@ export const randomExtendedNote = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), + noteTypeId = Fr.random(), }: Partial = {}) => { - return new ExtendedNote(note, owner, contractAddress, storageSlot, txHash); + return new ExtendedNote(note, owner, contractAddress, storageSlot, noteTypeId, txHash); }; diff --git a/yarn-project/circuit-types/src/notes/extended_note.ts b/yarn-project/circuit-types/src/notes/extended_note.ts index 8a2855bc7ab..48b9e53b0f4 100644 --- a/yarn-project/circuit-types/src/notes/extended_note.ts +++ b/yarn-project/circuit-types/src/notes/extended_note.ts @@ -17,6 +17,8 @@ export class ExtendedNote { public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ public storageSlot: Fr, + /** The type identifier of the note on the contract. */ + public noteTypeId: Fr, /** The hash of the tx the note was created in. */ public txHash: TxHash, ) {} @@ -27,6 +29,7 @@ export class ExtendedNote { this.owner.toBuffer(), this.contractAddress.toBuffer(), this.storageSlot.toBuffer(), + this.noteTypeId.toBuffer(), this.txHash.buffer, ]); } @@ -37,9 +40,10 @@ export class ExtendedNote { const owner = AztecAddress.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); const storageSlot = Fr.fromBuffer(reader); + const noteTypeId = Fr.fromBuffer(reader); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); - return new this(note, owner, contractAddress, storageSlot, txHash); + return new this(note, owner, contractAddress, storageSlot, noteTypeId, txHash); } toString() { diff --git a/yarn-project/cli/src/cmds/add_note.ts b/yarn-project/cli/src/cmds/add_note.ts index c70176159d7..988c2a45c12 100644 --- a/yarn-project/cli/src/cmds/add_note.ts +++ b/yarn-project/cli/src/cmds/add_note.ts @@ -9,13 +9,14 @@ export async function addNote( address: AztecAddress, contractAddress: AztecAddress, storageSlot: Fr, + noteTypeId: Fr, txHash: TxHash, noteFields: string[], rpcUrl: string, debugLogger: DebugLogger, ) { const note = new Note(parseFields(noteFields)); - const extendedNote = new ExtendedNote(note, address, contractAddress, storageSlot, txHash); + const extendedNote = new ExtendedNote(note, address, contractAddress, storageSlot, noteTypeId, txHash); const client = await createCompatibleClient(rpcUrl, debugLogger); await client.addNote(extendedNote); } diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 70215cf4251..72170602c88 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -396,12 +396,22 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .argument('
', 'The Aztec address of the note owner.', parseAztecAddress) .argument('', 'Aztec address of the contract.', parseAztecAddress) .argument('', 'The storage slot of the note.', parseField) + .argument('', 'The type ID of the note.', parseField) .argument('', 'The tx hash of the tx containing the note.', parseTxHash) .requiredOption('-n, --note [note...]', 'The members of a Note serialized as hex strings.', []) .addOption(pxeOption) - .action(async (address, contractAddress, storageSlot, txHash, options) => { + .action(async (address, contractAddress, storageSlot, noteTypeId, txHash, options) => { const { addNote } = await import('./cmds/add_note.js'); - await addNote(address, contractAddress, storageSlot, txHash, options.note, options.rpcUrl, debugLogger); + await addNote( + address, + contractAddress, + storageSlot, + noteTypeId, + txHash, + options.note, + options.rpcUrl, + debugLogger, + ); }); // Helper for users to decode hex strings into structs if needed. diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index fb5175d7722..dff36888301 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -109,8 +109,10 @@ describe('e2e_2_pxes', () => { expect(receipt.status).toEqual(TxStatus.MINED); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(balance), secretHash]); - const extendedNote = new ExtendedNote(note, recipient, contract.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote(note, recipient, contract.address, storageSlot, noteTypeId, receipt.txHash); await pxe.addNote(extendedNote); expect((await contract.methods.redeem_shield(recipient, balance, secret).send().wait()).status).toEqual( diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts index 9f462b241dc..bb4eb2e3954 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts @@ -88,8 +88,16 @@ describe('e2e_blacklist_token_contract', () => { const addPendingShieldNoteToPXE = async (accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) => { const storageSlot = new Fr(4); // The storage slot of `pending_shields` is 4. + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote(note, accounts[accountIndex].address, asset.address, storageSlot, txHash); + const extendedNote = new ExtendedNote( + note, + accounts[accountIndex].address, + asset.address, + storageSlot, + noteTypeId, + txHash, + ); await wallets[accountIndex].addNote(extendedNote); }; @@ -113,6 +121,7 @@ describe('e2e_blacklist_token_contract', () => { // Add the note const note = new Note([slowTree.address.toField()]); const storageSlot = new Fr(6); + const noteTypeId = new Fr(7010510110810078111116101n); // FieldNote for (const wallet of wallets) { const extendedNote = new ExtendedNote( @@ -120,6 +129,7 @@ describe('e2e_blacklist_token_contract', () => { wallet.getCompleteAddress().address, asset.address, storageSlot, + noteTypeId, receipt.txHash, ); await wallet.addNote(extendedNote); diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index d28ec8e086a..4efca1fbd6a 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -213,11 +213,13 @@ describe('e2e_cheat_codes', () => { // docs:start:pxe_add_note const note = new Note([new Fr(mintAmount), secretHash]); const pendingShieldStorageSlot = new Fr(5n); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote const extendedNote = new ExtendedNote( note, admin.address, token.address, pendingShieldStorageSlot, + noteTypeId, receipt.txHash, ); await pxe.addNote(extendedNote); diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index b39411d4206..ed34164fccb 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -25,6 +25,8 @@ import { setup } from './fixtures/utils.js'; describe('e2e_escrow_contract', () => { const pendingShieldsStorageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + let pxe: PXE; let wallet: AccountWallet; let recipientWallet: AccountWallet; @@ -82,7 +84,15 @@ describe('e2e_escrow_contract', () => { expect(receipt.status).toEqual(TxStatus.MINED); const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, owner, token.address, pendingShieldsStorageSlot, receipt.txHash); + + const extendedNote = new ExtendedNote( + note, + owner, + token.address, + pendingShieldsStorageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); expect( @@ -129,7 +139,14 @@ describe('e2e_escrow_contract', () => { expect(receipt.status).toEqual(TxStatus.MINED); const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, owner, token.address, pendingShieldsStorageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + owner, + token.address, + pendingShieldsStorageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); expect((await token.methods.redeem_shield(owner, mintAmount, secret).send().wait()).status).toEqual(TxStatus.MINED); diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index bc6baf42a99..7a7fa9d33c8 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -126,9 +126,18 @@ describe('e2e_lending_contract', () => { await Promise.all([a, b].map(waitForSuccess)); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(mintAmount), secretHash]); const txHash = await b.getTxHash(); - const extendedNote = new ExtendedNote(note, accounts[0].address, asset.address, storageSlot, txHash); + const extendedNote = new ExtendedNote( + note, + accounts[0].address, + asset.address, + storageSlot, + noteTypeId, + txHash, + ); await wallet.addNote(extendedNote); await waitForSuccess(asset.methods.redeem_shield(lendingAccount.address, mintAmount, secret).send()); diff --git a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts index 08acbdf58f7..c4d006bb135 100644 --- a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts +++ b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts @@ -66,8 +66,17 @@ describe('e2e_multiple_accounts_1_enc_key', () => { expect(receipt.status).toEqual(TxStatus.MINED); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(initialBalance), secretHash]); - const extendedNote = new ExtendedNote(note, accounts[0].address, token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + accounts[0].address, + token.address, + storageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); expect((await token.methods.redeem_shield(accounts[0], initialBalance, secret).send().wait()).status).toEqual( diff --git a/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts b/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts index c1838f854d6..ea27674a3cb 100644 --- a/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts +++ b/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts @@ -83,11 +83,14 @@ describe('e2e_non_contract_account', () => { // Add the note const note = new Note([new Fr(value)]); const storageSlot = new Fr(1); + const noteTypeId = new Fr(7010510110810078111116101n); // FieldNote + const extendedNote = new ExtendedNote( note, wallet.getCompleteAddress().address, contract.address, storageSlot, + noteTypeId, receipt.txHash, ); await wallet.addNote(extendedNote); diff --git a/yarn-project/end-to-end/src/e2e_persistence.test.ts b/yarn-project/end-to-end/src/e2e_persistence.test.ts index cd9c0de3998..8b6a7c95113 100644 --- a/yarn-project/end-to-end/src/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/e2e_persistence.test.ts @@ -342,7 +342,9 @@ async function addPendingShieldNoteToPXE( // The storage slot of `pending_shields` is 5. // TODO AlexG, this feels brittle const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote(note, wallet.getAddress(), asset, storageSlot, txHash); + const extendedNote = new ExtendedNote(note, wallet.getAddress(), asset, storageSlot, noteTypeId, txHash); await wallet.addNote(extendedNote); } diff --git a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts index e92b85608aa..be75c656781 100644 --- a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts +++ b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts @@ -77,8 +77,12 @@ describe('e2e_sandbox_example', () => { // Add the newly created "pending shield" note to PXE const pendingShieldsStorageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(initialSupply), aliceSecretHash]); - await pxe.addNote(new ExtendedNote(note, alice, contract.address, pendingShieldsStorageSlot, receipt.txHash)); + await pxe.addNote( + new ExtendedNote(note, alice, contract.address, pendingShieldsStorageSlot, noteTypeId, receipt.txHash), + ); // Make the tokens spendable by redeeming them using the secret (converts the "pending shield note" created above // to a "token note") @@ -145,7 +149,14 @@ describe('e2e_sandbox_example', () => { const bobPendingShield = new Note([new Fr(mintQuantity), bobSecretHash]); await pxe.addNote( - new ExtendedNote(bobPendingShield, bob, contract.address, pendingShieldsStorageSlot, mintPrivateReceipt.txHash), + new ExtendedNote( + bobPendingShield, + bob, + contract.address, + pendingShieldsStorageSlot, + noteTypeId, + mintPrivateReceipt.txHash, + ), ); await tokenContractBob.methods.redeem_shield(bob, mintQuantity, bobSecret).send().wait(); diff --git a/yarn-project/end-to-end/src/e2e_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_token_contract.test.ts index bd40ce3faac..f2fbc8e754b 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract.test.ts @@ -39,8 +39,17 @@ describe('e2e_token_contract', () => { const addPendingShieldNoteToPXE = async (accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) => { const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote(note, accounts[accountIndex].address, asset.address, storageSlot, txHash); + const extendedNote = new ExtendedNote( + note, + accounts[accountIndex].address, + asset.address, + storageSlot, + noteTypeId, + txHash, + ); await wallets[accountIndex].addNote(extendedNote); }; diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index 203fda7ed9a..db45440e2fe 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -48,8 +48,17 @@ describe('guides/dapp/testing', () => { const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait(); const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, recipientAddress, token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + recipientAddress, + token.address, + storageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); await token.methods.redeem_shield(recipientAddress, mintAmount, secret).send().wait(); @@ -83,8 +92,17 @@ describe('guides/dapp/testing', () => { const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait(); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, recipientAddress, token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + recipientAddress, + token.address, + storageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); await token.methods.redeem_shield(recipientAddress, mintAmount, secret).send().wait(); @@ -139,8 +157,17 @@ describe('guides/dapp/testing', () => { const receipt = await token.methods.mint_private(100n, secretHash).send().wait(); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, ownerAddress, token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + ownerAddress, + token.address, + storageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); await token.methods.redeem_shield(ownerAddress, 100n, secret).send().wait(); diff --git a/yarn-project/end-to-end/src/guides/up_quick_start.sh b/yarn-project/end-to-end/src/guides/up_quick_start.sh index cd6de58da45..cf87493a1b2 100755 --- a/yarn-project/end-to-end/src/guides/up_quick_start.sh +++ b/yarn-project/end-to-end/src/guides/up_quick_start.sh @@ -29,7 +29,7 @@ MINT_PRIVATE_OUTPUT=$(aztec-cli send mint_private \ MINT_PRIVATE_TX_HASH=$(echo "$MINT_PRIVATE_OUTPUT" | grep "Transaction hash:" | awk '{print $NF}') aztec-cli add-note \ - $ALICE $CONTRACT 5 $MINT_PRIVATE_TX_HASH \ + $ALICE $CONTRACT 5 84114971101151129711410111011678111116101 $MINT_PRIVATE_TX_HASH \ --note 1000 $SECRET_HASH aztec-cli send redeem_shield \ diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 894be127d2b..42388c673d1 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -74,8 +74,10 @@ describe('guides/writing_an_account_contract', () => { const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait(); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, address, token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote(note, address, token.address, storageSlot, noteTypeId, receipt.txHash); await pxe.addNote(extendedNote); await token.methods.redeem_shield({ address }, mintAmount, secret).send().wait(); diff --git a/yarn-project/end-to-end/src/sample-dapp/index.mjs b/yarn-project/end-to-end/src/sample-dapp/index.mjs index 36f2b82a6f4..f15c8d1c638 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.mjs @@ -38,8 +38,17 @@ async function mintPrivateFunds(pxe) { const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait(); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote + const note = new Note([new Fr(mintAmount), secretHash]); - const extendedNote = new ExtendedNote(note, owner.getAddress(), token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + owner.getAddress(), + token.address, + storageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); await token.methods.redeem_shield(owner.getAddress(), mintAmount, secret).send().wait(); diff --git a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs index ce8294ddbd3..b13e1dad951 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs @@ -31,8 +31,16 @@ describe('token', () => { const receipt = await token.methods.mint_private(initialBalance, secretHash).send().wait(); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote const note = new Note([new Fr(initialBalance), secretHash]); - const extendedNote = new ExtendedNote(note, owner.getAddress(), token.address, storageSlot, receipt.txHash); + const extendedNote = new ExtendedNote( + note, + owner.getAddress(), + token.address, + storageSlot, + noteTypeId, + receipt.txHash, + ); await pxe.addNote(extendedNote); await token.methods.redeem_shield({ address: owner.getAddress() }, initialBalance, secret).send().wait(); diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 6dc060470fa..59db47a2207 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -252,12 +252,15 @@ export const browserTestSuite = ( const mintPrivateReceipt = await token.methods.mint_private(initialBalance, secretHash).send().wait(); const storageSlot = new Fr(5); + + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); const note = new Note([new Fr(initialBalance), secretHash]); const extendedNote = new ExtendedNote( note, ownerAddress, token.address, storageSlot, + noteTypeId, mintPrivateReceipt.txHash, ); await pxe.addNote(extendedNote); diff --git a/yarn-project/end-to-end/src/shared/cli.ts b/yarn-project/end-to-end/src/shared/cli.ts index c9a8de89a0d..ee121151c0d 100644 --- a/yarn-project/end-to-end/src/shared/cli.ts +++ b/yarn-project/end-to-end/src/shared/cli.ts @@ -160,7 +160,7 @@ export const cliTestSuite = ( const txHashes = findMultipleInLogs(/Transaction Hash: ([0-9a-f]{64})/i); const mintPrivateTxHash = txHashes[txHashes.length - 1][1]; await run( - `add-note ${ownerAddress} ${contractAddress} 5 ${mintPrivateTxHash} --note ${INITIAL_BALANCE} ${secretHash}`, + `add-note ${ownerAddress} ${contractAddress} 5 84114971101151129711410111011678111116101 ${mintPrivateTxHash} --note ${INITIAL_BALANCE} ${secretHash}`, ); debug('Redeem tokens.'); diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index d72c2f1e8f2..70d4319ce95 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -425,8 +425,16 @@ export class CrossChainTestHarness { async addPendingShieldNoteToPXE(shieldAmount: bigint, secretHash: Fr, txHash: TxHash) { this.logger('Adding note to PXE'); const storageSlot = new Fr(5); + const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote const note = new Note([new Fr(shieldAmount), secretHash]); - const extendedNote = new ExtendedNote(note, this.ownerAddress, this.l2Token.address, storageSlot, txHash); + const extendedNote = new ExtendedNote( + note, + this.ownerAddress, + this.l2Token.address, + storageSlot, + noteTypeId, + txHash, + ); await this.pxeService.addNote(extendedNote); } diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr index c1f40dc56fd..219b8e501ee 100644 --- a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -89,6 +89,7 @@ contract AvmTest { _contract_address: AztecAddress, _nonce: Field, _storage_slot: Field, + _note_type_id: Field, _serialized_note: [Field; 1] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr b/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr index dac5c5cd2a2..5cdbbe83d5f 100644 --- a/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -65,6 +65,7 @@ contract Benchmarking { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr b/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr index 9c26425af7a..ef3b161cfdd 100644 --- a/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr @@ -156,6 +156,7 @@ contract CardGame { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr index 9e0e1d505bb..f557d06bb50 100644 --- a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr @@ -100,6 +100,7 @@ contract Child { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr b/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr index 3d3e85cfd95..11a9acecebc 100644 --- a/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr @@ -55,6 +55,7 @@ contract Counter { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr index d884a56a40f..86c91046462 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -237,6 +237,7 @@ contract DocsExample { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; CARD_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 1e3d22782bf..356e6421c21 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -77,8 +77,15 @@ impl NoteInterface for CardNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'CardNote')))" + 679711410078111116101 + } } diff --git a/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr b/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr index 741edd43d29..29137e82834 100644 --- a/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr @@ -65,6 +65,7 @@ contract EasyPrivateToken { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr b/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr index dec8f8d7ade..ea919f4eb7d 100644 --- a/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr @@ -71,6 +71,7 @@ contract EasyPrivateVoting { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index 3d98773e00d..1ac71be5690 100644 --- a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -103,10 +103,17 @@ impl NoteInterface for EcdsaPublicKeyNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'EcdsaPublicKeyNote')))" + 6999100115978011798108105997510112178111116101 + } } impl EcdsaPublicKeyNote { diff --git a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index bb250dc33b2..97042d3f7d6 100644 --- a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -86,6 +86,7 @@ contract EcdsaAccount { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] ) -> pub [Field; 4] { assert(storage_slot == storage.public_key.get_storage_slot()); diff --git a/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr b/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr index 094de197436..1bc3de696df 100644 --- a/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr @@ -53,6 +53,7 @@ contract Escrow { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; ADDRESS_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 7d8a67d2df4..913a0a5b161 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -258,6 +258,7 @@ contract InclusionProofs { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr b/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr index f8587fe21fc..64bab5a6e72 100644 --- a/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr @@ -312,6 +312,7 @@ contract Lending { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr b/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr index 65a6a25316d..6c09247b61a 100644 --- a/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr @@ -267,6 +267,7 @@ contract PendingCommitments { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr b/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr index b1ee56c8991..5d2ef98e196 100644 --- a/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr @@ -40,6 +40,7 @@ contract PriceFeed { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr index b77d6e86fa3..166c3279c75 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -96,6 +96,7 @@ contract SchnorrAccount { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; PUBLIC_KEY_NOTE_LEN] ) -> pub [Field; 4] { assert(storage_slot == storage.signing_public_key.get_storage_slot()); diff --git a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index f42930e2a90..d552a44f4a0 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -72,10 +72,17 @@ impl NoteInterface for PublicKeyNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'PublicKeyNote')))" + 8011798108105997510112178111116101 + } } impl PublicKeyNote { diff --git a/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr b/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr index 0a36cb11bfc..b8a0d2525d8 100644 --- a/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr @@ -145,6 +145,7 @@ contract SlowTree { _contract_address: AztecAddress, _nonce: Field, _storage_slot: Field, + _note_type_id: Field, _serialized_note: [Field; 4] ) -> pub [Field; 4] { [0x0d, 0x0e, 0x0a, 0x0d] diff --git a/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr index 0aba89b09d5..8c00312a3b7 100644 --- a/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -60,6 +60,7 @@ contract StatefulTest { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); diff --git a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr index 3a510118076..8aa168448b4 100644 --- a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr @@ -349,13 +349,14 @@ contract Test { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN] // must fit either a FieldNote or a ValueNote ) -> pub [Field; 4] { - if (storage_slot == storage.example_constant.get_storage_slot()) { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + + if (note_type_id == FieldNote::get_note_type_id()) { note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, serialized_note) } else { - // For ValueNotes created via write_value_to_storage let note_header = NoteHeader::new(contract_address, nonce, storage_slot); note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize_content, note_header, serialized_note) } diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr index af3d757b1be..0a0394289a1 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -334,9 +334,6 @@ contract TokenBlacklist { storage.public_balances.at(owner).read().value } - // Below this point is the stuff of nightmares. - // This should ideally not be required. What do we do if vastly different types of preimages? - // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. @@ -344,12 +341,14 @@ contract TokenBlacklist { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, preimage: [Field; TOKEN_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - if (storage_slot == storage.pending_shields.get_storage_slot()) { + + if (note_type_id == TransparentNote::get_note_type_id()) { note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize_content, note_header, preimage) - } else if (storage_slot == storage.slow_update.get_storage_slot()) { + } else if (note_type_id == FieldNote::get_note_type_id()) { note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, preimage) } else { note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, preimage) diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index a1a0f0810bd..a7e2a45f1ec 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -89,11 +89,18 @@ impl NoteInterface for TokenNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } - } + } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'TokenNote')))" + 8411110710111078111116101 + } } impl OwnedNote for TokenNote { diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr index 4117d289776..a444d27df26 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr @@ -57,6 +57,12 @@ impl NoteInterface for TransparentNote { fn broadcast(self, context: &mut PrivateContext, slot: Field) { assert(false, "TransparentNote does not support broadcast"); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'TransparentNote')))" + 84114971101151129711410111011678111116101 + } } impl TransparentNote { diff --git a/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr index 895beb1bc0c..bf03277ee1a 100644 --- a/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -161,6 +161,7 @@ contract TokenBridge { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_contract/src/main.nr index 1c21fd1f95a..f48b637ef6d 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/main.nr @@ -380,9 +380,6 @@ contract Token { } // docs:end:balance_of_public - // Below this point is the stuff of nightmares. - // This should ideally not be required. What do we do if vastly different types of serialized_notes? - // docs:start:compute_note_hash_and_nullifier // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. @@ -391,10 +388,12 @@ contract Token { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; TOKEN_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - if (storage_slot == storage.pending_shields.get_storage_slot()) { + + if (note_type_id == TransparentNote::get_note_type_id()) { note_utils::compute_note_hash_and_nullifier( TransparentNote::deserialize_content, note_header, diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr index f9e58497c86..2be2ecb3e00 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -89,11 +89,18 @@ impl NoteInterface for TokenNote { context, (*context).this_address(), slot, + Self::get_note_type_id(), encryption_pub_key, self.serialize_content(), ); } - } + } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'TokenNote')))" + 8411110710111078111116101 + } } impl OwnedNote for TokenNote { diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr index 4117d289776..a444d27df26 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr @@ -57,6 +57,12 @@ impl NoteInterface for TransparentNote { fn broadcast(self, context: &mut PrivateContext, slot: Field) { assert(false, "TransparentNote does not support broadcast"); } + + fn get_note_type_id() -> Field { + // TODO(#4519): autogenerate + // python -c "print(int(''.join(str(ord(c)) for c in 'TransparentNote')))" + 84114971101151129711410111011678111116101 + } } impl TransparentNote { diff --git a/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr b/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr index 32a078a9090..061250c8d2a 100644 --- a/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -233,6 +233,7 @@ contract Uniswap { contract_address: AztecAddress, nonce: Field, storage_slot: Field, + note_type_id: Field, serialized_note: [Field; 0] ) -> pub [Field; 4] { [0, 0, 0, 0] diff --git a/yarn-project/pxe/src/database/deferred_note_dao.test.ts b/yarn-project/pxe/src/database/deferred_note_dao.test.ts index f4e92964f45..457da7d81a4 100644 --- a/yarn-project/pxe/src/database/deferred_note_dao.test.ts +++ b/yarn-project/pxe/src/database/deferred_note_dao.test.ts @@ -9,6 +9,7 @@ export const randomDeferredNoteDao = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), + noteTypeId = Fr.random(), newCommitments = [Fr.random(), Fr.random()], dataStartIndexForTx = Math.floor(Math.random() * 100), }: Partial = {}) => { @@ -17,6 +18,7 @@ export const randomDeferredNoteDao = ({ note, contractAddress, storageSlot, + noteTypeId, txHash, newCommitments, dataStartIndexForTx, diff --git a/yarn-project/pxe/src/database/deferred_note_dao.ts b/yarn-project/pxe/src/database/deferred_note_dao.ts index 575c13bea4c..97f6a41bd21 100644 --- a/yarn-project/pxe/src/database/deferred_note_dao.ts +++ b/yarn-project/pxe/src/database/deferred_note_dao.ts @@ -17,6 +17,8 @@ export class DeferredNoteDao { public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ public storageSlot: Fr, + /** The type ID of the note on the contract. */ + public noteTypeId: Fr, /** The hash of the tx the note was created in. Equal to the first nullifier */ public txHash: TxHash, /** New commitments in this transaction, one of which belongs to this note */ @@ -31,6 +33,7 @@ export class DeferredNoteDao { this.note.toBuffer(), this.contractAddress.toBuffer(), this.storageSlot.toBuffer(), + this.noteTypeId.toBuffer(), this.txHash.toBuffer(), new Vector(this.newCommitments), this.dataStartIndexForTx, @@ -43,6 +46,7 @@ export class DeferredNoteDao { reader.readObject(Note), reader.readObject(AztecAddress), reader.readObject(Fr), + reader.readObject(Fr), reader.readObject(TxHash), reader.readVector(Fr), reader.readNumber(), diff --git a/yarn-project/pxe/src/database/note_dao.test.ts b/yarn-project/pxe/src/database/note_dao.test.ts index 113f2de5fba..5b499eb73d9 100644 --- a/yarn-project/pxe/src/database/note_dao.test.ts +++ b/yarn-project/pxe/src/database/note_dao.test.ts @@ -8,6 +8,7 @@ export const randomNoteDao = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), + noteTypeId = Fr.random(), nonce = Fr.random(), innerNoteHash = Fr.random(), siloedNullifier = Fr.random(), @@ -18,6 +19,7 @@ export const randomNoteDao = ({ note, contractAddress, storageSlot, + noteTypeId, txHash, nonce, innerNoteHash, diff --git a/yarn-project/pxe/src/database/note_dao.ts b/yarn-project/pxe/src/database/note_dao.ts index a8ae6e2e9f5..2255420fc57 100644 --- a/yarn-project/pxe/src/database/note_dao.ts +++ b/yarn-project/pxe/src/database/note_dao.ts @@ -15,6 +15,8 @@ export class NoteDao implements NoteData { public contractAddress: AztecAddress, /** The specific storage location of the note on the contract. */ public storageSlot: Fr, + /** The note type identifier for the contract. */ + public noteTypeId: Fr, /** The hash of the tx the note was created in. */ public txHash: TxHash, /** The nonce of the note. */ @@ -37,6 +39,7 @@ export class NoteDao implements NoteData { this.note.toBuffer(), this.contractAddress.toBuffer(), this.storageSlot.toBuffer(), + this.noteTypeId.toBuffer(), this.txHash.buffer, this.nonce.toBuffer(), this.innerNoteHash.toBuffer(), @@ -51,6 +54,7 @@ export class NoteDao implements NoteData { const note = Note.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); const storageSlot = Fr.fromBuffer(reader); + const noteTypeId = Fr.fromBuffer(reader); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); const nonce = Fr.fromBuffer(reader); const innerNoteHash = Fr.fromBuffer(reader); @@ -62,6 +66,7 @@ export class NoteDao implements NoteData { note, contractAddress, storageSlot, + noteTypeId, txHash, nonce, innerNoteHash, diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index adba0584460..ea3fab328b5 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -41,6 +41,7 @@ describe('Kernel Prover', () => { .map(() => ({ note: new Note([Fr.random(), Fr.random(), Fr.random()]), storageSlot: Fr.random(), + noteTypeId: Fr.random(), owner: { x: Fr.random(), y: Fr.random() }, })); diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index a08189e5a21..495626533bd 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -139,7 +139,7 @@ describe('Note Processor', () => { Promise.resolve({ innerNoteHash: Fr.random(), siloedNoteHash: Fr.random(), - uniqueSiloedNoteHash: computeMockNoteHash(args[3]), + uniqueSiloedNoteHash: computeMockNoteHash(args[4]), // args[4] is note innerNullifier: Fr.random(), }), ); diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index 4ed25627317..fdd768e08be 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -160,6 +160,7 @@ export class NoteProcessor { payload.note, payload.contractAddress, payload.storageSlot, + payload.noteTypeId, txHash, newCommitments, dataStartIndexForTx, @@ -254,8 +255,9 @@ export class NoteProcessor { const excludedIndices: Set = new Set(); const noteDaos: NoteDao[] = []; for (const deferredNote of deferredNoteDaos) { - const { note, contractAddress, storageSlot, txHash, newCommitments, dataStartIndexForTx } = deferredNote; - const payload = new L1NotePayload(note, contractAddress, storageSlot); + const { note, contractAddress, storageSlot, noteTypeId, txHash, newCommitments, dataStartIndexForTx } = + deferredNote; + const payload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); try { const noteDao = await produceNoteDao( diff --git a/yarn-project/pxe/src/note_processor/produce_note_dao.ts b/yarn-project/pxe/src/note_processor/produce_note_dao.ts index 80a2bdbe1df..a57f0ffb33e 100644 --- a/yarn-project/pxe/src/note_processor/produce_note_dao.ts +++ b/yarn-project/pxe/src/note_processor/produce_note_dao.ts @@ -42,6 +42,7 @@ export async function produceNoteDao( payload.note, payload.contractAddress, payload.storageSlot, + payload.noteTypeId, txHash, nonce, innerNoteHash, @@ -70,7 +71,7 @@ async function findNoteIndexAndNullifier( simulator: AcirSimulator, commitments: Fr[], txHash: TxHash, - { contractAddress, storageSlot, note }: L1NotePayload, + { contractAddress, storageSlot, noteTypeId, note }: L1NotePayload, excludedIndices: Set, ) { let commitmentIndex = 0; @@ -93,7 +94,7 @@ async function findNoteIndexAndNullifier( const expectedNonce = computeCommitmentNonce(firstNullifier, commitmentIndex); ({ innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier } = - await simulator.computeNoteHashAndNullifier(contractAddress, expectedNonce, storageSlot, note)); + await simulator.computeNoteHashAndNullifier(contractAddress, expectedNonce, storageSlot, noteTypeId, note)); if (commitment.equals(uniqueSiloedNoteHash)) { nonce = expectedNonce; break; diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 5191695675e..f08954f0131 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -266,7 +266,7 @@ export class PXEService implements PXE { } owner = completeAddresses.address; } - return new ExtendedNote(dao.note, owner, dao.contractAddress, dao.storageSlot, dao.txHash); + return new ExtendedNote(dao.note, owner, dao.contractAddress, dao.storageSlot, dao.noteTypeId, dao.txHash); }); return Promise.all(extendedNotes); } @@ -284,7 +284,13 @@ export class PXEService implements PXE { for (const nonce of nonces) { const { innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier } = - await this.simulator.computeNoteHashAndNullifier(note.contractAddress, nonce, note.storageSlot, note.note); + await this.simulator.computeNoteHashAndNullifier( + note.contractAddress, + nonce, + note.storageSlot, + note.noteTypeId, + note.note, + ); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) // This can always be `uniqueSiloedNoteHash` once notes added from public also include nonces. @@ -305,6 +311,7 @@ export class PXEService implements PXE { note.note, note.contractAddress, note.storageSlot, + note.noteTypeId, note.txHash, nonce, innerNoteHash, @@ -342,6 +349,7 @@ export class PXEService implements PXE { note.contractAddress, nonce, note.storageSlot, + note.noteTypeId, note.note, ); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index e73e6a09853..867a452fc87 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -200,9 +200,15 @@ export class Oracle { return returnData.concat(paddedZeros); } - notifyCreatedNote([storageSlot]: ACVMField[], note: ACVMField[], [innerNoteHash]: ACVMField[]): ACVMField { + notifyCreatedNote( + [storageSlot]: ACVMField[], + [noteTypeId]: ACVMField[], + note: ACVMField[], + [innerNoteHash]: ACVMField[], + ): ACVMField { this.typedOracle.notifyCreatedNote( fromACVMField(storageSlot), + fromACVMField(noteTypeId), note.map(fromACVMField), fromACVMField(innerNoteHash), ); @@ -243,6 +249,7 @@ export class Oracle { emitEncryptedLog( [contractAddress]: ACVMField[], [storageSlot]: ACVMField[], + [noteTypeId]: ACVMField[], [publicKeyX]: ACVMField[], [publicKeyY]: ACVMField[], log: ACVMField[], @@ -251,6 +258,7 @@ export class Oracle { this.typedOracle.emitEncryptedLog( AztecAddress.fromString(contractAddress), Fr.fromString(storageSlot), + Fr.fromString(noteTypeId), publicKey, log.map(fromACVMField), ); diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 04931241300..bd76ffad27b 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -147,7 +147,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - notifyCreatedNote(_storageSlot: Fr, _note: Fr[], _innerNoteHash: Fr): void { + notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr): void { throw new Error('Not available.'); } @@ -175,7 +175,13 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - emitEncryptedLog(_contractAddress: AztecAddress, _storageSlot: Fr, _publicKey: PublicKey, _log: Fr[]): void { + emitEncryptedLog( + _contractAddress: AztecAddress, + _storageSlot: Fr, + _noteTypeId: Fr, + _publicKey: PublicKey, + _log: Fr[], + ): void { throw new Error('Not available.'); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 5faea465242..6871bd89ca8 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -249,11 +249,12 @@ export class ClientExecutionContext extends ViewDataOracle { * It can be used in subsequent calls (or transactions when chaining txs is possible). * @param contractAddress - The contract address. * @param storageSlot - The storage slot. + * @param noteTypeId - The type ID of the note. * @param noteItems - The items to be included in a Note. * @param innerNoteHash - The inner note hash of the new note. * @returns */ - public notifyCreatedNote(storageSlot: Fr, noteItems: Fr[], innerNoteHash: Fr) { + public notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr) { const note = new Note(noteItems); this.noteCache.addNewNote({ contractAddress: this.contractAddress, @@ -265,6 +266,7 @@ export class ClientExecutionContext extends ViewDataOracle { }); this.newNotes.push({ storageSlot, + noteTypeId, note, }); } @@ -284,12 +286,13 @@ export class ClientExecutionContext extends ViewDataOracle { * Encrypt a note and emit it as a log. * @param contractAddress - The contract address of the note. * @param storageSlot - The storage slot the note is at. + * @param noteTypeId - The type ID of the note. * @param publicKey - The public key of the account that can decrypt the log. * @param log - The log contents. */ - public emitEncryptedLog(contractAddress: AztecAddress, storageSlot: Fr, publicKey: Point, log: Fr[]) { + public emitEncryptedLog(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, publicKey: Point, log: Fr[]) { const note = new Note(log); - const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot); + const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); const encryptedNote = l1NotePayload.toEncryptedBuffer(publicKey, this.curve); this.encryptedLogs.push(encryptedNote); } diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index eb8876ff92e..1a822e3eb51 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -13,6 +13,8 @@ export interface NoteAndSlot { note: Note; /** The storage slot of the note. */ storageSlot: Fr; + /** The note type identifier. */ + noteTypeId: Fr; } /** diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 74fc04355c4..537f1230654 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -263,7 +263,7 @@ describe('Private Execution test suite', () => { const mockFirstNullifier = new Fr(1111); let currentNoteIndex = 0n; - const buildNote = (amount: bigint, owner: AztecAddress, storageSlot = Fr.random()) => { + const buildNote = (amount: bigint, owner: AztecAddress, storageSlot: Fr, noteTypeId: Fr) => { // WARNING: this is not actually how nonces are computed! // For the purpose of this test we use a mocked firstNullifier and and a random number // to compute the nonce. Proper nonces are only enforced later by the kernel/later circuits @@ -279,6 +279,7 @@ describe('Private Execution test suite', () => { return { contractAddress, storageSlot, + noteTypeId, nonce, note, innerNoteHash, @@ -317,6 +318,7 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(newNote.noteTypeId).toEqual(new Fr(869710811710178111116101n)); // ValueNote const newCommitments = sideEffectArrayToValueArray( nonEmptySideEffects(result.callStackItem.publicInputs.newCommitments), @@ -325,7 +327,12 @@ describe('Private Execution test suite', () => { const [commitment] = newCommitments; expect(commitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, newNote.storageSlot, newNote.note), + await acirSimulator.computeInnerNoteHash( + contractAddress, + newNote.storageSlot, + newNote.noteTypeId, + newNote.note, + ), ); }); @@ -337,6 +344,7 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(newNote.noteTypeId).toEqual(new Fr(869710811710178111116101n)); // ValueNote const newCommitments = sideEffectArrayToValueArray( nonEmptySideEffects(result.callStackItem.publicInputs.newCommitments), @@ -345,7 +353,12 @@ describe('Private Execution test suite', () => { const [commitment] = newCommitments; expect(commitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, newNote.storageSlot, newNote.note), + await acirSimulator.computeInnerNoteHash( + contractAddress, + newNote.storageSlot, + newNote.noteTypeId, + newNote.note, + ), ); }); @@ -356,11 +369,13 @@ describe('Private Execution test suite', () => { const storageSlot = computeSlotForMapping(new Fr(1n), owner); const recipientStorageSlot = computeSlotForMapping(new Fr(1n), recipient); - const notes = [buildNote(60n, owner, storageSlot), buildNote(80n, owner, storageSlot)]; + const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + + const notes = [buildNote(60n, owner, storageSlot, noteTypeId), buildNote(80n, owner, storageSlot, noteTypeId)]; oracle.getNotes.mockResolvedValue(notes); const consumedNotes = await asyncMap(notes, ({ nonce, note }) => - acirSimulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note), + acirSimulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, noteTypeId, note), ); await insertLeaves(consumedNotes.map(n => n.siloedNoteHash)); @@ -377,6 +392,7 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(2); const [changeNote, recipientNote] = result.newNotes; expect(recipientNote.storageSlot).toEqual(recipientStorageSlot); + expect(recipientNote.noteTypeId).toEqual(noteTypeId); const newCommitments = sideEffectArrayToValueArray(result.callStackItem.publicInputs.newCommitments).filter( field => !field.equals(Fr.ZERO), @@ -385,10 +401,10 @@ describe('Private Execution test suite', () => { const [changeNoteCommitment, recipientNoteCommitment] = newCommitments; expect(recipientNoteCommitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, recipientStorageSlot, recipientNote.note), + await acirSimulator.computeInnerNoteHash(contractAddress, recipientStorageSlot, noteTypeId, recipientNote.note), ); expect(changeNoteCommitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, changeNote.note), + await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, noteTypeId, changeNote.note), ); expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); @@ -408,12 +424,13 @@ describe('Private Execution test suite', () => { const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create'); const storageSlot = computeSlotForMapping(new Fr(1n), owner); + const noteTypeId = new Fr(869710811710178111116101n); // ValueNote - const notes = [buildNote(balance, owner, storageSlot)]; + const notes = [buildNote(balance, owner, storageSlot, noteTypeId)]; oracle.getNotes.mockResolvedValue(notes); const consumedNotes = await asyncMap(notes, ({ nonce, note }) => - acirSimulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note), + acirSimulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, noteTypeId, note), ); await insertLeaves(consumedNotes.map(n => n.siloedNoteHash)); @@ -916,7 +933,14 @@ describe('Private Execution test suite', () => { const commitment = newCommitments[0]; const storageSlot = computeSlotForMapping(new Fr(1n), owner); - const innerNoteHash = await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, noteAndSlot.note); + const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + + const innerNoteHash = await acirSimulator.computeInnerNoteHash( + contractAddress, + storageSlot, + noteTypeId, + noteAndSlot.note, + ); expect(commitment).toEqual(innerNoteHash); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) @@ -984,9 +1008,13 @@ describe('Private Execution test suite', () => { const execGetThenNullify = result.nestedExecutions[1]; const getNotesAfterNullify = result.nestedExecutions[2]; + const storageSlot = computeSlotForMapping(new Fr(1n), owner); + const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + expect(execInsert.newNotes).toHaveLength(1); const noteAndSlot = execInsert.newNotes[0]; - expect(noteAndSlot.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(noteAndSlot.storageSlot).toEqual(storageSlot); + expect(noteAndSlot.noteTypeId).toEqual(noteTypeId); expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); @@ -996,8 +1024,12 @@ describe('Private Execution test suite', () => { expect(newCommitments).toHaveLength(1); const commitment = newCommitments[0]; - const storageSlot = computeSlotForMapping(new Fr(1n), owner); - const innerNoteHash = await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, noteAndSlot.note); + const innerNoteHash = await acirSimulator.computeInnerNoteHash( + contractAddress, + noteAndSlot.storageSlot, + noteAndSlot.noteTypeId, + noteAndSlot.note, + ); expect(commitment).toEqual(innerNoteHash); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) @@ -1040,9 +1072,13 @@ describe('Private Execution test suite', () => { contractAddress, }); + const storageSlot = computeSlotForMapping(new Fr(1n), owner); + const noteTypeId = new Fr(869710811710178111116101n); // ValueNote + expect(result.newNotes).toHaveLength(1); const noteAndSlot = result.newNotes[0]; - expect(noteAndSlot.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(noteAndSlot.storageSlot).toEqual(storageSlot); + expect(noteAndSlot.noteTypeId).toEqual(noteTypeId); expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); @@ -1052,9 +1088,13 @@ describe('Private Execution test suite', () => { expect(newCommitments).toHaveLength(1); const commitment = newCommitments[0]; - const storageSlot = computeSlotForMapping(new Fr(1n), owner); expect(commitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, noteAndSlot.note), + await acirSimulator.computeInnerNoteHash( + contractAddress, + storageSlot, + noteAndSlot.noteTypeId, + noteAndSlot.note, + ), ); // read requests should be empty diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index ed7b3df1369..603ea6ddf76 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -42,6 +42,7 @@ describe('Simulator', () => { const contractAddress = AztecAddress.random(); const nonce = Fr.random(); const storageSlot = Fr.random(); + const noteTypeId = Fr.random(); const createNote = (amount = 123n) => new Note([new Fr(amount), owner.toField(), Fr.random()]); @@ -59,7 +60,7 @@ describe('Simulator', () => { ownerNullifierSecretKey.high, ]); - const result = await simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note); + const result = await simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, noteTypeId, note); expect(result).toEqual({ innerNoteHash, @@ -74,10 +75,28 @@ describe('Simulator', () => { const note = createNote(); await expect( - simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note), + simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, noteTypeId, note), ).rejects.toThrowError(/Mandatory implementation of "compute_note_hash_and_nullifier" missing/); }); + it('throw if "compute_note_hash_and_nullifier" has the wrong number of parameters', async () => { + const note = createNote(); + + const modifiedArtifact: FunctionArtifactWithDebugMetadata = { + ...artifact, + parameters: artifact.parameters.slice(1), + }; + oracle.getFunctionArtifactByName.mockResolvedValue(modifiedArtifact); + + await expect( + simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, noteTypeId, note), + ).rejects.toThrowError( + new RegExp( + `Expected 5 parameters in mandatory implementation of "compute_note_hash_and_nullifier", but found 4 in noir contract ${contractAddress}.`, + ), + ); + }); + it('throw if a note has more fields than "compute_note_hash_and_nullifier" can process', async () => { const note = createNote(); const wrongPreimageLength = note.length - 1; @@ -102,7 +121,7 @@ describe('Simulator', () => { oracle.getFunctionArtifactByName.mockResolvedValue(modifiedArtifact); await expect( - simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note), + simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, noteTypeId, note), ).rejects.toThrowError( new RegExp(`"compute_note_hash_and_nullifier" can only handle a maximum of ${wrongPreimageLength} fields`), ); diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index be2852dce69..57cfda70177 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -159,10 +159,17 @@ export class AcirSimulator { * @param contractAddress - The address of the contract. * @param nonce - The nonce of the note hash. * @param storageSlot - The storage slot. + * @param noteTypeId - The note type identifier. * @param note - The note. * @returns The nullifier. */ - public async computeNoteHashAndNullifier(contractAddress: AztecAddress, nonce: Fr, storageSlot: Fr, note: Note) { + public async computeNoteHashAndNullifier( + contractAddress: AztecAddress, + nonce: Fr, + storageSlot: Fr, + noteTypeId: Fr, + note: Note, + ) { const artifact: FunctionArtifactWithDebugMetadata | undefined = await this.db.getFunctionArtifactByName( contractAddress, 'compute_note_hash_and_nullifier', @@ -173,6 +180,14 @@ export class AcirSimulator { ); } + if (artifact.parameters.length != 5) { + throw new Error( + `Expected 5 parameters in mandatory implementation of "compute_note_hash_and_nullifier", but found ${ + artifact.parameters.length + } in noir contract ${contractAddress.toString()}.`, + ); + } + const maxNoteFields = (artifact.parameters[artifact.parameters.length - 1].type as ArrayType).length; if (maxNoteFields < note.items.length) { throw new Error( @@ -185,7 +200,7 @@ export class AcirSimulator { const execRequest: FunctionCall = { to: contractAddress, functionData: FunctionData.empty(), - args: encodeArguments(artifact, [contractAddress, nonce, storageSlot, extendedNoteItems]), + args: encodeArguments(artifact, [contractAddress, nonce, storageSlot, noteTypeId, extendedNoteItems]), }; const [innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier] = (await this.runUnconstrained( @@ -206,11 +221,18 @@ export class AcirSimulator { * Computes the inner note hash of a note, which contains storage slot and the custom note hash. * @param contractAddress - The address of the contract. * @param storageSlot - The storage slot. + * @param noteTypeId - The note type identifier. * @param note - The note. * @returns The note hash. */ - public async computeInnerNoteHash(contractAddress: AztecAddress, storageSlot: Fr, note: Note) { - const { innerNoteHash } = await this.computeNoteHashAndNullifier(contractAddress, Fr.ZERO, storageSlot, note); + public async computeInnerNoteHash(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, note: Note) { + const { innerNoteHash } = await this.computeNoteHashAndNullifier( + contractAddress, + Fr.ZERO, + storageSlot, + noteTypeId, + note, + ); return innerNoteHash; } @@ -219,11 +241,24 @@ export class AcirSimulator { * @param contractAddress - The address of the contract. * @param nonce - The nonce of the note hash. * @param storageSlot - The storage slot. + * @param noteTypeId - The note type identifier. * @param note - The note. * @returns The note hash. */ - public async computeUniqueSiloedNoteHash(contractAddress: AztecAddress, nonce: Fr, storageSlot: Fr, note: Note) { - const { uniqueSiloedNoteHash } = await this.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note); + public async computeUniqueSiloedNoteHash( + contractAddress: AztecAddress, + nonce: Fr, + storageSlot: Fr, + noteTypeId: Fr, + note: Note, + ) { + const { uniqueSiloedNoteHash } = await this.computeNoteHashAndNullifier( + contractAddress, + nonce, + storageSlot, + noteTypeId, + note, + ); return uniqueSiloedNoteHash; } @@ -232,11 +267,24 @@ export class AcirSimulator { * @param contractAddress - The address of the contract. * @param nonce - The nonce of the note hash. * @param storageSlot - The storage slot. + * @param noteTypeId - The note type identifier. * @param note - The note. * @returns The note hash. */ - public async computeSiloedNoteHash(contractAddress: AztecAddress, nonce: Fr, storageSlot: Fr, note: Note) { - const { siloedNoteHash } = await this.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note); + public async computeSiloedNoteHash( + contractAddress: AztecAddress, + nonce: Fr, + storageSlot: Fr, + noteTypeId: Fr, + note: Note, + ) { + const { siloedNoteHash } = await this.computeNoteHashAndNullifier( + contractAddress, + nonce, + storageSlot, + noteTypeId, + note, + ); return siloedNoteHash; } @@ -245,11 +293,24 @@ export class AcirSimulator { * @param contractAddress - The address of the contract. * @param nonce - The nonce of the unique note hash. * @param storageSlot - The storage slot. + * @param noteTypeId - The note type identifier. * @param note - The note. * @returns The note hash. */ - public async computeInnerNullifier(contractAddress: AztecAddress, nonce: Fr, storageSlot: Fr, note: Note) { - const { innerNullifier } = await this.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note); + public async computeInnerNullifier( + contractAddress: AztecAddress, + nonce: Fr, + storageSlot: Fr, + noteTypeId: Fr, + note: Note, + ) { + const { innerNullifier } = await this.computeNoteHashAndNullifier( + contractAddress, + nonce, + storageSlot, + noteTypeId, + note, + ); return innerNullifier; } }