diff --git a/barretenberg/ts/src/types/fields.ts b/barretenberg/ts/src/types/fields.ts index 9305f0d6142..ef9d9188ced 100644 --- a/barretenberg/ts/src/types/fields.ts +++ b/barretenberg/ts/src/types/fields.ts @@ -15,7 +15,7 @@ export class Fr { const valueBigInt = typeof value === 'bigint' ? value : toBigIntBE(value); if (valueBigInt > Fr.MAX_VALUE) { - throw new Error(`Fr out of range: ${valueBigInt}`); + throw new Error(`Value 0x${valueBigInt.toString(16)} is greater or equal to field modulus.`); } this.value = typeof value === 'bigint' ? toBufferBE(value) : value; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index e95771baa30..07e0cb74b12 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -60,7 +60,9 @@ mod test { impl NoteInterface for AddressNote { fn compute_note_content_hash(self) -> Field {1} - fn get_note_type_id() -> Field {1} + fn get_note_type_id() -> Field { + 1 + } fn get_header(self) -> NoteHeader { self.header} diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index a427e7cc298..50b65919f1e 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -451,6 +451,7 @@ dependencies = [ "noirc_errors", "noirc_frontend", "regex", + "tiny-keccak", ] [[package]] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index ed70066af22..a99a654aeed 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -16,4 +16,4 @@ noirc_errors.workspace = true iter-extended.workspace = true convert_case = "0.6.0" regex = "1.10" - +tiny-keccak = { version = "2.0.0", features = ["keccak"] } diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index c91ba4e069a..3ace22a89c3 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -11,7 +11,10 @@ use noirc_frontend::{ Type, }; +use acvm::AcirField; use regex::Regex; +// TODO(#7165): nuke the following dependency from here and Cargo.toml +use tiny_keccak::{Hasher, Keccak}; use crate::{ chained_dep, @@ -97,7 +100,6 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt .collect::, _>>()?; let [note_serialized_len, note_bytes_len]: [_; 2] = note_interface_generics.try_into().unwrap(); - let note_type_id = note_type_id(¬e_type); // Automatically inject the header field if it's not present let (header_field_name, _) = if let Some(existing_header) = @@ -184,8 +186,9 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt } if !check_trait_method_implemented(trait_impl, "get_note_type_id") { + let note_type_id = compute_note_type_id(¬e_type); let get_note_type_id_fn = - generate_note_get_type_id(¬e_type_id, note_interface_impl_span)?; + generate_get_note_type_id(note_type_id, note_interface_impl_span)?; trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); } @@ -324,16 +327,17 @@ fn generate_note_set_header( // Automatically generate the note type id getter method. The id itself its calculated as the concatenation // of the conversion of the characters in the note's struct name to unsigned integers. -fn generate_note_get_type_id( - note_type_id: &str, +fn generate_get_note_type_id( + note_type_id: u32, impl_span: Option, ) -> Result { + // TODO(#7165): replace {} with dep::aztec::protocol_types::abis::note_selector::compute_note_selector(\"{}\") in the function source below let function_source = format!( " - fn get_note_type_id() -> Field {{ - {} - }} - ", + fn get_note_type_id() -> Field {{ + {} + }} + ", note_type_id ) .to_string(); @@ -387,7 +391,7 @@ fn generate_note_properties_struct( // Generate the deserialize_content method as // -// fn deserialize_content(serialized_note: [Field; NOTE_SERILIZED_LEN]) -> Self { +// fn deserialize_content(serialized_note: [Field; NOTE_SERIALIZED_LEN]) -> Self { // NoteType { // note_field1: serialized_note[0] as Field, // note_field2: NoteFieldType2::from_field(serialized_note[1])... @@ -525,10 +529,10 @@ fn generate_note_exports_global( let struct_source = format!( " #[abi(notes)] - global {0}_EXPORTS: (Field, str<{1}>) = ({2},\"{0}\"); + global {0}_EXPORTS: (Field, str<{1}>) = (0x{2},\"{0}\"); ", note_type, - note_type_id.len(), + note_type.len(), note_type_id ) .to_string(); @@ -685,10 +689,18 @@ fn generate_note_deserialize_content_source( .to_string() } +// TODO(#7165): nuke this function // Utility function to generate the note type id as a Field -fn note_type_id(note_type: &str) -> String { +fn compute_note_type_id(note_type: &str) -> u32 { // TODO(#4519) Improve automatic note id generation and assignment - note_type.chars().map(|c| (c as u32).to_string()).collect::>().join("") + let mut keccak = Keccak::v256(); + let mut result = [0u8; 32]; + keccak.update(note_type.as_bytes()); + keccak.finalize(&mut result); + // Take the first 4 bytes of the hash and convert them to an integer + // If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well + let num_bytes_per_note_type_id = 4; + u32::from_be_bytes(result[0..num_bytes_per_note_type_id].try_into().unwrap()) } pub fn inject_note_exports( @@ -717,29 +729,42 @@ pub fn inject_note_exports( }, file_id, ))?; - let init_function = + let get_note_type_id_function = context.def_interner.function(&func_id).block(&context.def_interner); - let init_function_statement_id = init_function.statements().first().ok_or(( - AztecMacroError::CouldNotExportStorageLayout { - span: None, - secondary_message: Some(format!( - "Could not retrieve note id statement from function for note {}", - note.borrow().name.0.contents - )), - }, - file_id, - ))?; - let note_id_statement = context.def_interner.statement(init_function_statement_id); + let get_note_type_id_statement_id = + get_note_type_id_function.statements().first().ok_or(( + AztecMacroError::CouldNotExportStorageLayout { + span: None, + secondary_message: Some(format!( + "Could not retrieve note id statement from function for note {}", + note.borrow().name.0.contents + )), + }, + file_id, + ))?; + let note_type_id_statement = + context.def_interner.statement(get_note_type_id_statement_id); - let note_id_value = match note_id_statement { + let note_type_id = match note_type_id_statement { HirStatement::Expression(expression_id) => { match context.def_interner.expression(&expression_id) { HirExpression::Literal(HirLiteral::Integer(value, _)) => Ok(value), + HirExpression::Literal(_) => Err(( + AztecMacroError::CouldNotExportStorageLayout { + span: None, + secondary_message: Some( + "note_type_id statement must be a literal integer expression" + .to_string(), + ), + }, + file_id, + )), _ => Err(( AztecMacroError::CouldNotExportStorageLayout { span: None, secondary_message: Some( - "note_id statement must be a literal expression".to_string(), + "note_type_id statement must be a literal expression" + .to_string(), ), }, file_id, @@ -747,9 +772,10 @@ pub fn inject_note_exports( } } _ => Err(( - AztecMacroError::CouldNotAssignStorageSlots { + AztecMacroError::CouldNotExportStorageLayout { + span: None, secondary_message: Some( - "note_id statement must be an expression".to_string(), + "note_type_id statement must be an expression".to_string(), ), }, file_id, @@ -757,7 +783,7 @@ pub fn inject_note_exports( }?; let global = generate_note_exports_global( ¬e.borrow().name.0.contents, - ¬e_id_value.to_string(), + ¬e_type_id.to_hex(), ) .map_err(|err| (err, file_id))?; diff --git a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs index 71dd1b18761..e959c61732a 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use acvm::acir::circuit::ErrorSelector; +use acvm::AcirField; use iter_extended::vecmap; use noirc_abi::{Abi, AbiErrorType, AbiParameter, AbiReturnType, AbiType, AbiValue}; use noirc_frontend::ast::Visibility; @@ -107,9 +108,7 @@ pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpres }, HirLiteral::Bool(value) => AbiValue::Boolean { value }, HirLiteral::Str(value) => AbiValue::String { value }, - HirLiteral::Integer(field, sign) => { - AbiValue::Integer { value: field.to_string(), sign } - } + HirLiteral::Integer(field, sign) => AbiValue::Integer { value: field.to_hex(), sign }, _ => unreachable!("Literal cannot be used in the abi"), }, _ => unreachable!("Type cannot be used in the abi {:?}", expression), diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index f3bd1a2aada..4e3d71e4d8f 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -52,6 +52,7 @@ export { waitForAccountSynch, waitForPXE, } from './utils/index.js'; +export { NoteSelector } from '@aztec/foundation/abi'; export { createPXEClient } from './rpc_clients/index.js'; diff --git a/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts b/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts index c54a9674c4d..2b6871e885e 100644 --- a/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts +++ b/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts @@ -25,6 +25,7 @@ import { GrumpkinScalar, Point, } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client'; /** @@ -53,6 +54,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false) Point, TxExecutionRequest, TxHash, + NoteSelector, }, { Tx, SimulatedTx, TxReceipt, EncryptedNoteL2BlockL2Logs, UnencryptedL2BlockL2Logs, NullifierMembershipWitness }, false, diff --git a/yarn-project/builder/src/contract-interface-gen/typescript.ts b/yarn-project/builder/src/contract-interface-gen/typescript.ts index 5fc2828c500..d092f4b0688 100644 --- a/yarn-project/builder/src/contract-interface-gen/typescript.ts +++ b/yarn-project/builder/src/contract-interface-gen/typescript.ts @@ -225,7 +225,7 @@ function generateNotesGetter(input: ContractArtifact) { .map( ([name, { id }]) => `${name}: { - id: new Fr(${id.toBigInt()}n), + id: new NoteSelector(${id.value}), }`, ) .join(',\n'); @@ -281,11 +281,7 @@ function generateEvents(events: any[] | undefined) { if (payload === undefined) { return undefined; } - if ( - !eventSelector.equals( - EventSelector.fromField(payload.eventTypeId), - ) - ) { + if (!eventSelector.equals(payload.eventTypeId)) { return undefined; } if (payload.event.items.length !== fieldsLength) { @@ -349,14 +345,14 @@ import { DeployMethod, EthAddress, EthAddressLike, + EventSelector, FieldLike, Fr, - EventSelector, - FunctionSelector, FunctionSelectorLike, L1EventPayload, loadContractArtifact, NoirCompiledContract, + NoteSelector, Point, PublicKey, Wallet, diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts index 939ca41ea65..d6a3dbfd110 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts @@ -1,5 +1,6 @@ import { Fr, GrumpkinScalar } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { NoteSelector } from '@aztec/foundation/abi'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { Note } from '../payload.js'; @@ -20,10 +21,10 @@ describe('encrypt log incoming body', () => { const viewingPubKey = grumpkin.mul(Grumpkin.generator, viewingSecretKey); const note = Note.random(); - const noteTypeId = Fr.random(); const storageSlot = Fr.random(); + const noteTypeId = NoteSelector.random(); - const body = new EncryptedNoteLogIncomingBody(noteTypeId, storageSlot, note); + const body = new EncryptedNoteLogIncomingBody(storageSlot, noteTypeId, note); const encrypted = body.computeCiphertext(ephSecretKey, viewingPubKey); @@ -44,7 +45,7 @@ describe('encrypt log incoming body', () => { const viewingPubKey = grumpkin.mul(Grumpkin.generator, viewingSecretKey); const note = new Note([new Fr(1), new Fr(2), new Fr(3)]); - const noteTypeId = new Fr(1); + const noteTypeId = new NoteSelector(1); const storageSlot = new Fr(2); const body = new EncryptedNoteLogIncomingBody(storageSlot, noteTypeId, note); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts index 83bd9edb479..2edaba57db4 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.ts @@ -1,11 +1,12 @@ import { Fr, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { Note } from '../payload.js'; import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; export class EncryptedNoteLogIncomingBody extends EncryptedLogIncomingBody { - constructor(public storageSlot: Fr, public noteTypeId: Fr, public note: Note) { + constructor(public storageSlot: Fr, public noteTypeId: NoteSelector, public note: Note) { super(); } @@ -16,7 +17,8 @@ export class EncryptedNoteLogIncomingBody extends EncryptedLogIncomingBody { */ public toBuffer(): Buffer { const noteBufferWithoutLength = this.note.toBuffer().subarray(4); - return serializeToBuffer(this.storageSlot, this.noteTypeId, noteBufferWithoutLength); + // Note: We serialize note type to field first because that's how it's done in Noir + return serializeToBuffer(this.storageSlot, this.noteTypeId.toField(), noteBufferWithoutLength); } /** @@ -28,7 +30,7 @@ export class EncryptedNoteLogIncomingBody extends EncryptedLogIncomingBody { public static fromBuffer(buf: Buffer): EncryptedNoteLogIncomingBody { const reader = BufferReader.asReader(buf); const storageSlot = Fr.fromBuffer(reader); - const noteTypeId = Fr.fromBuffer(reader); + const noteTypeId = NoteSelector.fromField(Fr.fromBuffer(reader)); // 2 Fields (storage slot and note type id) are not included in the note buffer const fieldsInNote = reader.getLength() / 32 - 2; diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.test.ts index ea7f49391b6..1598b9c02f1 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.test.ts @@ -1,4 +1,5 @@ import { AztecAddress, KeyValidationRequest, computeOvskApp, derivePublicKeyFromSecretKey } from '@aztec/circuits.js'; +import { EventSelector } from '@aztec/foundation/abi'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; @@ -29,7 +30,7 @@ describe('L1 Event Payload', () => { randomness = Fr.random(); maskedContractAddress = pedersenHash([contractAddress, randomness], 0); - payload = new L1EventPayload(Event.random(), contractAddress, randomness, Fr.random()); + payload = new L1EventPayload(Event.random(), contractAddress, randomness, EventSelector.random()); ovskM = GrumpkinScalar.random(); ivskM = GrumpkinScalar.random(); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.ts index e3cd80ba061..741a0128475 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_event_payload.ts @@ -1,4 +1,5 @@ import { AztecAddress, type GrumpkinPrivateKey, type KeyValidationRequest, type PublicKey } from '@aztec/circuits.js'; +import { EventSelector } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -25,9 +26,9 @@ export class L1EventPayload extends L1Payload { */ public randomness: Fr, /** - * Type identifier for the underlying event, (calculated as a function selector). + * Type identifier for the underlying event. */ - public eventTypeId: Fr, + public eventTypeId: EventSelector, ) { super(); } @@ -43,7 +44,7 @@ export class L1EventPayload extends L1Payload { reader.readObject(Event), reader.readObject(AztecAddress), Fr.fromBuffer(reader), - Fr.fromBuffer(reader), + reader.readObject(EventSelector), ); } @@ -60,7 +61,7 @@ export class L1EventPayload extends L1Payload { * @returns A random L1EventPayload object. */ static random() { - return new L1EventPayload(Event.random(), AztecAddress.random(), Fr.random(), Fr.random()); + return new L1EventPayload(Event.random(), AztecAddress.random(), Fr.random(), EventSelector.random()); } public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovKeys: KeyValidationRequest) { @@ -70,7 +71,7 @@ export class L1EventPayload extends L1Payload { recipient, ivpk, ovKeys, - new EncryptedEventLogIncomingBody(this.randomness, this.eventTypeId, this.event), + new EncryptedEventLogIncomingBody(this.randomness, this.eventTypeId.toField(), this.event), ); } @@ -100,9 +101,13 @@ export class L1EventPayload extends L1Payload { EncryptedEventLogIncomingBody.fromCiphertext, ); + // We instantiate selector before checking the address because instantiating the selector validates that + // the selector is valid (and that's the preferred way of detecting decryption failure). + const selector = EventSelector.fromField(incomingBody.eventTypeId); + this.ensureMatchedMaskedContractAddress(address, incomingBody.randomness, encryptedLog.maskedContractAddress); - return new L1EventPayload(incomingBody.event, address, incomingBody.randomness, incomingBody.eventTypeId); + return new L1EventPayload(incomingBody.event, address, incomingBody.randomness, selector); } /** @@ -131,8 +136,12 @@ export class L1EventPayload extends L1Payload { EncryptedEventLogIncomingBody.fromCiphertext, ); + // We instantiate selector before checking the address because instantiating the selector validates that + // the selector is valid (and that's the preferred way of detecting decryption failure). + const selector = EventSelector.fromField(incomingBody.eventTypeId); + this.ensureMatchedMaskedContractAddress(address, incomingBody.randomness, encryptedLog.maskedContractAddress); - return new L1EventPayload(incomingBody.event, address, incomingBody.randomness, incomingBody.eventTypeId); + return new L1EventPayload(incomingBody.event, address, incomingBody.randomness, selector); } } diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts index ee28010c1bf..b0dadca6ffe 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts @@ -1,4 +1,5 @@ import { AztecAddress, type GrumpkinPrivateKey, type KeyValidationRequest, type PublicKey } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -28,7 +29,7 @@ export class L1NotePayload extends L1Payload { /** * Type identifier for the underlying note, required to determine how to compute its hash and nullifier. */ - public noteTypeId: Fr, + public noteTypeId: NoteSelector, ) { super(); } @@ -44,7 +45,7 @@ export class L1NotePayload extends L1Payload { reader.readObject(Note), reader.readObject(AztecAddress), Fr.fromBuffer(reader), - Fr.fromBuffer(reader), + reader.readObject(NoteSelector), ); } @@ -62,7 +63,7 @@ export class L1NotePayload extends L1Payload { * @returns A random L1NotePayload object. */ static random(contract = AztecAddress.random()) { - return new L1NotePayload(Note.random(), contract, Fr.random(), Fr.random()); + return new L1NotePayload(Note.random(), contract, Fr.random(), NoteSelector.random()); } public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovKeys: KeyValidationRequest) { diff --git a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts index c5c7968c965..a7b736db73c 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts @@ -1,4 +1,5 @@ import { AztecAddress, KeyValidationRequest, computeOvskApp, derivePublicKeyFromSecretKey } from '@aztec/circuits.js'; +import { EventSelector } from '@aztec/foundation/abi'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; @@ -86,7 +87,7 @@ describe('L1 Event Payload', () => { randomness = Fr.random(); maskedContractAddress = pedersenHash([contractAddress, randomness], 0); - const payload = new L1EventPayload(Event.random(), contractAddress, randomness, Fr.random()); + const payload = new L1EventPayload(Event.random(), contractAddress, randomness, EventSelector.random()); ovskM = GrumpkinScalar.random(); ivskM = GrumpkinScalar.random(); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts index 4904479acdb..c4d2ec5fe73 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.ts @@ -78,8 +78,6 @@ export class TaggedLog { ivsk: GrumpkinPrivateKey, payloadType: typeof L1NotePayload | typeof L1EventPayload = L1NotePayload, ): TaggedLog | undefined { - // Right now heavily abusing that we will likely fail if bad decryption - // as some field will likely end up not being in the field etc. try { if (payloadType === L1EventPayload) { const reader = BufferReader.asReader((data as EncryptedL2Log).data); @@ -96,7 +94,17 @@ export class TaggedLog { const payload = L1NotePayload.decryptAsIncoming(reader.readToEnd(), ivsk); return new TaggedLog(payload, incomingTag, outgoingTag); } - } catch (e) { + } catch (e: any) { + // Following error messages are expected to occur when decryption fails + if ( + !e.message.endsWith('is greater or equal to field modulus.') && + !e.message.startsWith('Invalid AztecAddress length') && + !e.message.startsWith('Selector must fit in') && + !e.message.startsWith('Attempted to read beyond buffer length') + ) { + // If we encounter an unexpected error, we rethrow it + throw e; + } return; } } @@ -116,8 +124,6 @@ export class TaggedLog { ovsk: GrumpkinPrivateKey, payloadType: typeof L1NotePayload | typeof L1EventPayload = L1NotePayload, ) { - // Right now heavily abusing that we will likely fail if bad decryption - // as some field will likely end up not being in the field etc. try { if (payloadType === L1EventPayload) { const reader = BufferReader.asReader((data as EncryptedL2Log).data); @@ -133,7 +139,17 @@ export class TaggedLog { const payload = L1NotePayload.decryptAsOutgoing(reader.readToEnd(), ovsk); return new TaggedLog(payload, incomingTag, outgoingTag); } - } catch (e) { + } catch (e: any) { + // Following error messages are expected to occur when decryption fails + if ( + !e.message.endsWith('is greater or equal to field modulus.') && + !e.message.startsWith('Invalid AztecAddress length') && + !e.message.startsWith('Selector must fit in') && + !e.message.startsWith('Attempted to read beyond buffer length') + ) { + // If we encounter an unexpected error, we rethrow it + throw e; + } return; } } diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index a3a86bb6235..99df4ebcfa5 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -19,7 +19,7 @@ import { makeCombinedConstantData, makePublicCallRequest, } from '@aztec/circuits.js/testing'; -import { type ContractArtifact } from '@aztec/foundation/abi'; +import { type ContractArtifact, NoteSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { times } from '@aztec/foundation/collection'; import { randomBytes } from '@aztec/foundation/crypto'; @@ -220,7 +220,7 @@ export const randomExtendedNote = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), - noteTypeId = Fr.random(), + noteTypeId = NoteSelector.random(), }: Partial = {}) => { 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 caee60e8be9..bf91e2dc49d 100644 --- a/yarn-project/circuit-types/src/notes/extended_note.ts +++ b/yarn-project/circuit-types/src/notes/extended_note.ts @@ -1,4 +1,5 @@ import { AztecAddress, Fr } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { BufferReader } from '@aztec/foundation/serialize'; import { Note } from '../logs/l1_payload/payload.js'; @@ -18,7 +19,7 @@ export class ExtendedNote { /** 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, + public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. */ public txHash: TxHash, ) {} @@ -33,6 +34,7 @@ export class ExtendedNote { this.txHash.buffer, ]); } + static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); @@ -40,7 +42,7 @@ export class ExtendedNote { const owner = AztecAddress.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); const storageSlot = Fr.fromBuffer(reader); - const noteTypeId = Fr.fromBuffer(reader); + const noteTypeId = reader.readObject(NoteSelector); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); return new this(note, owner, contractAddress, storageSlot, noteTypeId, txHash); diff --git a/yarn-project/cli/src/cmds/add_note.ts b/yarn-project/cli/src/cmds/add_note.ts index f6359bd5c1c..68debccd90c 100644 --- a/yarn-project/cli/src/cmds/add_note.ts +++ b/yarn-project/cli/src/cmds/add_note.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, type Fr } from '@aztec/aztec.js'; +import { type AztecAddress, type Fr, type NoteSelector } from '@aztec/aztec.js'; import { ExtendedNote, Note, type TxHash } from '@aztec/circuit-types'; import { type DebugLogger } from '@aztec/foundation/log'; @@ -9,7 +9,7 @@ export async function addNote( address: AztecAddress, contractAddress: AztecAddress, storageSlot: Fr, - noteTypeId: Fr, + noteTypeId: NoteSelector, txHash: TxHash, noteFields: string[], rpcUrl: string, diff --git a/yarn-project/cli/src/inspect.ts b/yarn-project/cli/src/inspect.ts index f8ff880f1ee..53c424680fc 100644 --- a/yarn-project/cli/src/inspect.ts +++ b/yarn-project/cli/src/inspect.ts @@ -142,7 +142,7 @@ export async function inspectTx( function inspectNote(note: ExtendedNote, artifactMap: ArtifactMap, log: LogFn, text = 'Note') { const artifact = artifactMap[note.contractAddress.toString()]; const contract = artifact?.name ?? note.contractAddress.toString(); - const type = artifact?.notes[note.noteTypeId.toString()]?.typ ?? note.noteTypeId.toShortString(); + const type = artifact?.notes[note.noteTypeId.toString()]?.typ ?? note.noteTypeId.toField().toShortString(); log(` ${text} type ${type} at ${contract}`); log(` Owner: ${toFriendlyAddress(note.owner, artifactMap)}`); for (const field of note.note.items) { diff --git a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts index d96397f05c8..1d465504b8b 100644 --- a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts @@ -25,10 +25,12 @@ const pageLogger = createDebugLogger('aztec:e2e_aztec_browser.js:web:page'); * 2) go to `yarn-project/end-to-end` and build the web packed package with `yarn build:web`, * 3) start anvil: `anvil`, * 4) if you intend to use a remotely running environment then export the URL of your PXE e.g. `export PXE_URL='http://localhost:8080'` - * 7) go to `yarn-project/end-to-end` and run the test: `yarn test aztec_js_browser` + * 5) go to `yarn-project/end-to-end` and run the test: `yarn test aztec_js_browser` + * 6) If you get dependency error run `apt install libssn3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libxdamage1 libxkbcommon0 libpango-1.0-0 libcairo2`. * - * NOTE: If you see the logs spammed with unexpected logs there is probably a chrome process with a webpage + * NOTE 1: If you see the logs spammed with unexpected logs there is probably a chrome process with a webpage * unexpectedly running in the background. Kill it with `killall chrome` + * NOTE 2: Don't forget to run `yarn build:web` once you make changes! */ const setupApp = async () => { diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index b2fbe9d1dac..02122e165d1 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -1,5 +1,6 @@ import { type AccountWalletWithSecretKey, type AztecNode, Fr, L1EventPayload, TaggedLog } from '@aztec/aztec.js'; import { deriveMasterIncomingViewingSecretKey } from '@aztec/circuits.js'; +import { EventSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; import { type ExampleEvent0, type ExampleEvent1, TestLogContract } from '@aztec/noir-contracts.js'; @@ -50,7 +51,7 @@ describe('Logs', () => { expect(decryptedLog0?.payload.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedLog0?.payload.randomness).toStrictEqual(randomness[0]); expect(decryptedLog0?.payload.eventTypeId).toStrictEqual( - new Fr(0x00000000000000000000000000000000000000000000000000000000aa533f60), + EventSelector.fromField(new Fr(0x00000000000000000000000000000000000000000000000000000000aa533f60)), ); // We decode our event into the event type @@ -73,7 +74,7 @@ describe('Logs', () => { expect(decryptedLog1?.payload.contractAddress).toStrictEqual(testLogContract.address); expect(decryptedLog1?.payload.randomness).toStrictEqual(randomness[1]); expect(decryptedLog1?.payload.eventTypeId).toStrictEqual( - new Fr(0x00000000000000000000000000000000000000000000000000000000d1be0447), + EventSelector.fromField(new Fr(0x00000000000000000000000000000000000000000000000000000000d1be0447)), ); // We check our second event, which is a different type diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index 2b608086604..9e996fc4249 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -2,6 +2,7 @@ import { inflate } from 'pako'; import { type Fr } from '../fields/fields.js'; import { type FunctionSelector } from './function_selector.js'; +import { type NoteSelector } from './note_selector.js'; /** * A basic value. @@ -275,7 +276,7 @@ export type ContractNote = { /** * Note identifier */ - id: Fr; + id: NoteSelector; /** * Type of the note (e.g., 'TransparentNote') */ diff --git a/yarn-project/foundation/src/abi/index.ts b/yarn-project/foundation/src/abi/index.ts index 476d3da8850..cab81b750c4 100644 --- a/yarn-project/foundation/src/abi/index.ts +++ b/yarn-project/foundation/src/abi/index.ts @@ -1,7 +1,8 @@ export * from './abi.js'; export * from './buffer.js'; +export * from './decoder.js'; export * from './encoder.js'; export * from './event_selector.js'; -export * from './decoder.js'; export * from './function_selector.js'; +export * from './note_selector.js'; export * from './utils.js'; diff --git a/yarn-project/foundation/src/abi/note_selector.ts b/yarn-project/foundation/src/abi/note_selector.ts new file mode 100644 index 00000000000..392399f7ee1 --- /dev/null +++ b/yarn-project/foundation/src/abi/note_selector.ts @@ -0,0 +1,73 @@ +import { toBigIntBE } from '../bigint-buffer/index.js'; +import { randomBytes } from '../crypto/index.js'; +import { type Fr } from '../fields/fields.js'; +import { BufferReader } from '../serialize/buffer_reader.js'; +import { TypeRegistry } from '../serialize/type_registry.js'; +import { Selector } from './selector.js'; + +/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ + +/** Note selector branding */ +export interface NoteSelector { + /** Brand. */ + _branding: 'NoteSelector'; +} + +/** A note selector is the first 4 bytes of the hash of a note signature. */ +export class NoteSelector extends Selector { + /** + * Deserializes from a buffer or reader, corresponding to a write in cpp. + * @param buffer - Buffer or BufferReader to read from. + * @returns The Selector. + */ + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + const value = Number(toBigIntBE(reader.readBytes(Selector.SIZE))); + return new NoteSelector(value); + } + + static fromString(buf: string) { + const withoutPrefix = buf.replace(/^0x/i, ''); + const buffer = Buffer.from(withoutPrefix, 'hex'); + return NoteSelector.fromBuffer(buffer); + } + + /** + * Converts a field to selector. + * @param fr - The field to convert. + * @returns The selector. + */ + static fromField(fr: Fr) { + return new NoteSelector(Number(fr.toBigInt())); + } + + /** + * Creates an empty selector. + * @returns An empty selector. + */ + static empty() { + return new NoteSelector(0); + } + + /** + * Creates a random selector. + * @returns A random selector. + */ + static random() { + return NoteSelector.fromBuffer(randomBytes(Selector.SIZE)); + } + + toJSON() { + return { + type: 'NoteSelector', + value: this.toString(), + }; + } + + static fromJSON(json: any): NoteSelector { + return NoteSelector.fromString(json.value); + } +} + +// For deserializing JSON. +TypeRegistry.register('NoteSelector', NoteSelector); 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 d3c1e5d520b..efe57f5a681 100644 --- a/yarn-project/pxe/src/database/deferred_note_dao.test.ts +++ b/yarn-project/pxe/src/database/deferred_note_dao.test.ts @@ -1,5 +1,6 @@ import { Note, randomTxHash } from '@aztec/circuit-types'; import { AztecAddress, Fr, Point } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { randomInt } from '@aztec/foundation/crypto'; import { DeferredNoteDao } from './deferred_note_dao.js'; @@ -10,7 +11,7 @@ export const randomDeferredNoteDao = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), - noteTypeId = Fr.random(), + noteTypeId = NoteSelector.random(), newNoteHashes = [Fr.random(), Fr.random()], dataStartIndexForTx = randomInt(100), }: Partial = {}) => { diff --git a/yarn-project/pxe/src/database/deferred_note_dao.ts b/yarn-project/pxe/src/database/deferred_note_dao.ts index 6e73db1e237..d1d0c551209 100644 --- a/yarn-project/pxe/src/database/deferred_note_dao.ts +++ b/yarn-project/pxe/src/database/deferred_note_dao.ts @@ -1,5 +1,6 @@ import { Note, TxHash } from '@aztec/circuit-types'; import { AztecAddress, Fr, Point, type PublicKey, Vector } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; /** @@ -18,7 +19,7 @@ export class DeferredNoteDao { /** 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, + public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. Equal to the first nullifier */ public txHash: TxHash, /** New note hashes in this transaction, one of which belongs to this note */ @@ -46,7 +47,7 @@ export class DeferredNoteDao { reader.readObject(Note), reader.readObject(AztecAddress), reader.readObject(Fr), - reader.readObject(Fr), + reader.readObject(NoteSelector), reader.readObject(TxHash), reader.readVector(Fr), reader.readNumber(), diff --git a/yarn-project/pxe/src/database/incoming_note_dao.test.ts b/yarn-project/pxe/src/database/incoming_note_dao.test.ts index ae8d562a381..f20e957fc1b 100644 --- a/yarn-project/pxe/src/database/incoming_note_dao.test.ts +++ b/yarn-project/pxe/src/database/incoming_note_dao.test.ts @@ -1,5 +1,6 @@ import { Note, randomTxHash } from '@aztec/circuit-types'; import { AztecAddress, Fr, Point } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { IncomingNoteDao } from './incoming_note_dao.js'; @@ -8,7 +9,7 @@ export const randomIncomingNoteDao = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), - noteTypeId = Fr.random(), + noteTypeId = NoteSelector.random(), nonce = Fr.random(), innerNoteHash = Fr.random(), siloedNullifier = Fr.random(), diff --git a/yarn-project/pxe/src/database/incoming_note_dao.ts b/yarn-project/pxe/src/database/incoming_note_dao.ts index 6db39e1b455..0a128a74259 100644 --- a/yarn-project/pxe/src/database/incoming_note_dao.ts +++ b/yarn-project/pxe/src/database/incoming_note_dao.ts @@ -1,5 +1,6 @@ import { Note, TxHash } from '@aztec/circuit-types'; import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type NoteData } from '@aztec/simulator'; @@ -16,7 +17,7 @@ export class IncomingNoteDao implements NoteData { /** The specific storage location of the note on the contract. */ public storageSlot: Fr, /** The note type identifier for the contract. */ - public noteTypeId: Fr, + public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. */ public txHash: TxHash, /** The nonce of the note. */ @@ -57,8 +58,8 @@ export class IncomingNoteDao 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 noteTypeId = reader.readObject(NoteSelector); + const txHash = reader.readObject(TxHash); const nonce = Fr.fromBuffer(reader); const innerNoteHash = Fr.fromBuffer(reader); const siloedNullifier = Fr.fromBuffer(reader); diff --git a/yarn-project/pxe/src/database/outgoing_note_dao.test.ts b/yarn-project/pxe/src/database/outgoing_note_dao.test.ts index 166a5e9b51b..9e7241760ff 100644 --- a/yarn-project/pxe/src/database/outgoing_note_dao.test.ts +++ b/yarn-project/pxe/src/database/outgoing_note_dao.test.ts @@ -1,5 +1,6 @@ import { Note, randomTxHash } from '@aztec/circuit-types'; import { AztecAddress, Fr, Point } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { OutgoingNoteDao } from './outgoing_note_dao.js'; @@ -8,7 +9,7 @@ export const randomOutgoingNoteDao = ({ contractAddress = AztecAddress.random(), txHash = randomTxHash(), storageSlot = Fr.random(), - noteTypeId = Fr.random(), + noteTypeId = NoteSelector.random(), nonce = Fr.random(), innerNoteHash = Fr.random(), index = Fr.random().toBigInt(), diff --git a/yarn-project/pxe/src/database/outgoing_note_dao.ts b/yarn-project/pxe/src/database/outgoing_note_dao.ts index e7e2b8c263d..03075f9f7df 100644 --- a/yarn-project/pxe/src/database/outgoing_note_dao.ts +++ b/yarn-project/pxe/src/database/outgoing_note_dao.ts @@ -1,5 +1,6 @@ import { Note, TxHash } from '@aztec/circuit-types'; import { AztecAddress, Fr, Point, type PublicKey } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -15,7 +16,7 @@ export class OutgoingNoteDao { /** The specific storage location of the note on the contract. */ public storageSlot: Fr, /** The note type identifier for the contract. */ - public noteTypeId: Fr, + public noteTypeId: NoteSelector, /** The hash of the tx the note was created in. */ public txHash: TxHash, /** The nonce of the note. */ @@ -50,7 +51,7 @@ export class OutgoingNoteDao { const note = Note.fromBuffer(reader); const contractAddress = AztecAddress.fromBuffer(reader); const storageSlot = Fr.fromBuffer(reader); - const noteTypeId = Fr.fromBuffer(reader); + const noteTypeId = reader.readObject(NoteSelector); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); const nonce = Fr.fromBuffer(reader); const innerNoteHash = Fr.fromBuffer(reader); 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 e96547fbbd7..e60353cd405 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -21,6 +21,7 @@ import { makeRecursiveProof, } from '@aztec/circuits.js'; import { makeTxRequest } from '@aztec/circuits.js/testing'; +import { NoteSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; @@ -45,7 +46,7 @@ describe('Kernel Prover', () => { .map(() => ({ note: new Note([Fr.random(), Fr.random(), Fr.random()]), storageSlot: Fr.random(), - noteTypeId: Fr.random(), + noteTypeId: NoteSelector.random(), owner: { x: Fr.random(), y: 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 61b1a820ba0..859309439d0 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -151,18 +151,17 @@ export class NoteProcessor { const outgoingTaggedNote = TaggedLog.decryptAsOutgoing(log.data, ovskM)!; if (incomingTaggedNote || outgoingTaggedNote) { - // TODO(#7053): Re-enable this check - // if ( - // incomingTaggedNote && - // outgoingTaggedNote && - // !incomingTaggedNote.payload.equals(outgoingTaggedNote.payload) - // ) { - // throw new Error( - // `Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify( - // incomingTaggedNote.payload, - // )}, Outgoing: ${JSON.stringify(outgoingTaggedNote.payload)}`, - // ); - // } + if ( + incomingTaggedNote && + outgoingTaggedNote && + !incomingTaggedNote.payload.equals(outgoingTaggedNote.payload) + ) { + throw new Error( + `Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify( + incomingTaggedNote.payload, + )}, Outgoing: ${JSON.stringify(outgoingTaggedNote.payload)}`, + ); + } const payload = incomingTaggedNote?.payload || outgoingTaggedNote?.payload; diff --git a/yarn-project/pxe/src/pxe_http/pxe_http_server.ts b/yarn-project/pxe/src/pxe_http/pxe_http_server.ts index b8e2500c57f..f429337c763 100644 --- a/yarn-project/pxe/src/pxe_http/pxe_http_server.ts +++ b/yarn-project/pxe/src/pxe_http/pxe_http_server.ts @@ -18,6 +18,7 @@ import { UnencryptedL2BlockL2Logs, } from '@aztec/circuit-types'; import { FunctionSelector } from '@aztec/circuits.js'; +import { NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; @@ -49,6 +50,7 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer { L2Block, TxEffect, LogId, + NoteSelector, }, { SimulatedTx, Tx, TxReceipt, EncryptedNoteL2BlockL2Logs, UnencryptedL2BlockL2Logs, NullifierMembershipWitness }, ['start', 'stop'], diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 5422eea4f00..cf351c5bf9b 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -35,13 +35,7 @@ import { getContractClassFromArtifact, } from '@aztec/circuits.js'; import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash'; -import { - type ContractArtifact, - type DecodedReturn, - EventSelector, - FunctionSelector, - encodeArguments, -} from '@aztec/foundation/abi'; +import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { type Fq, Fr, type Point } from '@aztec/foundation/fields'; import { SerialQueue } from '@aztec/foundation/fifo'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; @@ -859,7 +853,7 @@ export class PXEService implements PXE { if (visibleEvent.payload === undefined) { return undefined; } - if (!EventSelector.fromField(visibleEvent.payload.eventTypeId).equals(eventMetadata.eventSelector)) { + if (!visibleEvent.payload.eventTypeId.equals(eventMetadata.eventSelector)) { return undefined; } if (visibleEvent.payload.event.items.length !== eventMetadata.fieldNames.length) { diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 9655a0103cd..d2cd8ddf7d1 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -1,6 +1,6 @@ import { MerkleTreeId, UnencryptedL2Log } from '@aztec/circuit-types'; import { KeyValidationRequest } from '@aztec/circuits.js'; -import { EventSelector, FunctionSelector } from '@aztec/foundation/abi'; +import { EventSelector, FunctionSelector, NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, Point } from '@aztec/foundation/fields'; @@ -252,7 +252,7 @@ export class Oracle { ): ACVMField { this.typedOracle.notifyCreatedNote( fromACVMField(storageSlot), - fromACVMField(noteTypeId), + NoteSelector.fromField(fromACVMField(noteTypeId)), note.map(fromACVMField), fromACVMField(innerNoteHash), +counter, @@ -365,7 +365,7 @@ export class Oracle { const encLog = this.typedOracle.computeEncryptedNoteLog( AztecAddress.fromString(contractAddress), Fr.fromString(storageSlot), - Fr.fromString(noteTypeId), + NoteSelector.fromField(Fr.fromString(noteTypeId)), ovKeys, ivpkM, preimage.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 887a9b5f601..690ccf8ac86 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -16,7 +16,7 @@ import { type PrivateCallStackItem, type PublicCallRequest, } from '@aztec/circuits.js'; -import { type FunctionSelector } from '@aztec/foundation/abi'; +import { type FunctionSelector, type NoteSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { type ContractInstance } from '@aztec/types/contracts'; @@ -164,7 +164,13 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('getNotes'); } - notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { + notifyCreatedNote( + _storageSlot: Fr, + _noteTypeId: NoteSelector, + _note: Fr[], + _innerNoteHash: Fr, + _counter: number, + ): void { throw new OracleMethodNotAvailableError('notifyCreatedNote'); } @@ -219,7 +225,7 @@ export abstract class TypedOracle { computeEncryptedNoteLog( _contractAddress: AztecAddress, _storageSlot: Fr, - _noteTypeId: Fr, + _noteTypeId: NoteSelector, _ovKeys: KeyValidationRequest, _ivpkM: PublicKey, _preimage: Fr[], diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index ab66af72af1..7da0e48928d 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -22,7 +22,13 @@ import { } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash'; -import { type FunctionAbi, type FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi'; +import { + EventSelector, + type FunctionAbi, + type FunctionArtifact, + type NoteSelector, + countArgumentsSize, +} from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; @@ -288,7 +294,7 @@ export class ClientExecutionContext extends ViewDataOracle { */ public override notifyCreatedNote( storageSlot: Fr, - noteTypeId: Fr, + noteTypeId: NoteSelector, noteItems: Fr[], innerNoteHash: Fr, counter: number, @@ -382,7 +388,7 @@ export class ClientExecutionContext extends ViewDataOracle { preimage: Fr[], ) { const event = new Event(preimage); - const l1EventPayload = new L1EventPayload(event, contractAddress, randomness, eventTypeId); + const l1EventPayload = new L1EventPayload(event, contractAddress, randomness, EventSelector.fromField(eventTypeId)); const taggedEvent = new TaggedLog(l1EventPayload); const ephSk = GrumpkinScalar.random(); @@ -404,7 +410,7 @@ export class ClientExecutionContext extends ViewDataOracle { public override computeEncryptedNoteLog( contractAddress: AztecAddress, storageSlot: Fr, - noteTypeId: Fr, + noteTypeId: NoteSelector, ovKeys: KeyValidationRequest, ivpkM: Point, preimage: Fr[], diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index 0328e04ede3..518b3ea7ed1 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -8,6 +8,7 @@ import { type UnencryptedL2Log, } from '@aztec/circuit-types'; import { type IsEmpty, type PrivateCallStackItem, PublicCallRequest, sortByCounter } from '@aztec/circuits.js'; +import { type NoteSelector } from '@aztec/foundation/abi'; import { type Fr } from '@aztec/foundation/fields'; import { type ACVMField } from '../acvm/index.js'; @@ -21,7 +22,7 @@ export interface NoteAndSlot { /** The storage slot of the note. */ storageSlot: Fr; /** The note type identifier. */ - noteTypeId: Fr; + noteTypeId: NoteSelector; } export class CountedLog implements IsEmpty { diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 478685fe7d6..c46153495fb 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -33,7 +33,13 @@ import { } from '@aztec/circuits.js'; import { computeNoteHashNonce, computeSecretHash, computeVarArgsHash } from '@aztec/circuits.js/hash'; import { makeHeader } from '@aztec/circuits.js/testing'; -import { type FunctionArtifact, FunctionSelector, encodeArguments, getFunctionArtifact } from '@aztec/foundation/abi'; +import { + type FunctionArtifact, + FunctionSelector, + type NoteSelector, + encodeArguments, + getFunctionArtifact, +} from '@aztec/foundation/abi'; import { asyncMap } from '@aztec/foundation/async-map'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { times } from '@aztec/foundation/collection'; @@ -326,7 +332,7 @@ describe('Private Execution test suite', () => { const mockFirstNullifier = new Fr(1111); let currentNoteIndex = 0n; - const buildNote = (amount: bigint, ownerNpkMHash: Fr, storageSlot: Fr, noteTypeId: Fr) => { + const buildNote = (amount: bigint, ownerNpkMHash: Fr, storageSlot: Fr, noteTypeId: NoteSelector) => { // 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 diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index 0be77660a6a..e72c4ef7e97 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -5,6 +5,7 @@ import { type FunctionArtifact, FunctionSelector, FunctionType, + type NoteSelector, encodeArguments, } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -140,7 +141,7 @@ export class AcirSimulator { contractAddress: AztecAddress, nonce: Fr, storageSlot: Fr, - noteTypeId: Fr, + noteTypeId: NoteSelector, computeNullifier: boolean, note: Note, ) { @@ -210,7 +211,12 @@ export class AcirSimulator { * @param note - The note. * @returns The note hash. */ - public async computeInnerNoteHash(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, note: Note) { + public async computeInnerNoteHash( + contractAddress: AztecAddress, + storageSlot: Fr, + noteTypeId: NoteSelector, + note: Note, + ) { const { innerNoteHash } = await this.computeNoteHashAndOptionallyANullifier( contractAddress, Fr.ZERO, diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index bdf5273f232..567bf0a656d 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -36,7 +36,13 @@ import { } from '@aztec/circuits.js'; import { Aes128, Schnorr } from '@aztec/circuits.js/barretenberg'; import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash'; -import { type ContractArtifact, type FunctionAbi, FunctionSelector, countArgumentsSize } from '@aztec/foundation/abi'; +import { + type ContractArtifact, + type FunctionAbi, + FunctionSelector, + type NoteSelector, + countArgumentsSize, +} from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; @@ -392,7 +398,7 @@ export class TXE implements TypedOracle { return Promise.resolve(notes); } - notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr, counter: number) { + notifyCreatedNote(storageSlot: Fr, noteTypeId: NoteSelector, noteItems: Fr[], innerNoteHash: Fr, counter: number) { const note = new Note(noteItems); this.noteCache.addNewNote( { @@ -479,7 +485,7 @@ export class TXE implements TypedOracle { computeEncryptedNoteLog( contractAddress: AztecAddress, storageSlot: Fr, - noteTypeId: Fr, + noteTypeId: NoteSelector, ovKeys: KeyValidationRequest, ivpkM: Point, preimage: Fr[], diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index aef0d8e3d49..de25cc81299 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -10,6 +10,7 @@ import { getContractInstanceFromDeployParams, } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; +import { NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { type Logger } from '@aztec/foundation/log'; import { KeyStore } from '@aztec/key-store'; @@ -410,7 +411,7 @@ export class TXEService { ) { this.typedOracle.notifyCreatedNote( fromSingle(storageSlot), - fromSingle(noteTypeId), + NoteSelector.fromField(fromSingle(noteTypeId)), fromArray(note), fromSingle(innerNoteHash), fromSingle(counter).toNumber(), @@ -533,7 +534,7 @@ export class TXEService { const encLog = this.typedOracle.computeEncryptedNoteLog( AztecAddress.fromString(fromSingle(contractAddress).toString()), Fr.fromString(fromSingle(storageSlot).toString()), - Fr.fromString(fromSingle(noteTypeId).toString()), + NoteSelector.fromField(Fr.fromString(fromSingle(noteTypeId).toString())), ovKeys, ivpkM, fromArray(preimage), diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 9fff2b21b6a..91b0eb30ba8 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -8,6 +8,7 @@ import { type FunctionArtifact, FunctionType, type IntegerValue, + NoteSelector, type StructValue, type TypedStructFieldValue, } from '@aztec/foundation/abi'; @@ -57,6 +58,9 @@ export function contractArtifactFromBuffer(buffer: Buffer): ContractArtifact { if (key === 'bytecode' && typeof value === 'string') { return Buffer.from(value, 'base64'); } + if (typeof value === 'object' && value !== null && value.type === 'NoteSelector') { + return new NoteSelector(Number(value.value)); + } if (typeof value === 'object' && value !== null && value.type === 'Fr') { return new Fr(BigInt(value.value)); } @@ -252,7 +256,8 @@ function getNoteTypes(input: NoirCompiledContract) { return notes.reduce((acc: Record, note) => { const name = note.fields[1].value as string; - const id = new Fr(BigInt(note.fields[0].value)); + // Note id is encoded as a hex string + const id = NoteSelector.fromField(Fr.fromString(note.fields[0].value)); acc[name] = { id, typ: name,