From 2b8b2c3bcbed425027f343bd2b90fd6380e2ddc4 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 29 Apr 2024 16:32:57 +0100 Subject: [PATCH] feat: squashing transient note hashes and nullifiers (#6059) - Update the algorithm for squashing transient note hashes and nullifiers. Associate each note hash with a `nullifier_counter` to help identifying the pair. - Use specific `NoteHash` and `Nullifier` structs instead of `SideEffect` and `SideEffectLinkedToNoteHash`. - Move code constructing `PrivateKernelCircuitPublicInputs` from `common` to a separate file. --- .../aztec/src/context/private_context.nr | 18 +- .../aztec/src/context/public_context.nr | 16 +- .../aztec-nr/aztec/src/note/lifecycle.nr | 8 +- .../aztec-nr/aztec/src/oracle/notes.nr | 28 +- .../aztec/src/state_vars/private_set.nr | 8 +- .../crates/private-kernel-lib/src/common.nr | 195 ++---------- .../kernel_circuit_public_inputs_composer.nr | 104 +++---- .../crates/private-kernel-lib/src/lib.nr | 1 + ...e_kernel_circuit_public_inputs_composer.nr | 240 +++++++++++++++ .../src/private_kernel_init.nr | 89 +++--- .../src/private_kernel_inner.nr | 149 ++++++---- .../src/private_kernel_tail.nr | 208 ++++++++----- .../src/private_kernel_tail_to_public.nr | 194 +++++++----- .../crates/public-kernel-lib/src/common.nr | 22 +- .../src/public_kernel_app_logic.nr | 24 +- .../crates/reset-kernel-lib/src/lib.nr | 1 + ...llifier_non_existent_read_request_reset.nr | 4 +- .../private_validation_request_processor.nr | 6 +- .../public_validation_request_processor.nr | 9 +- .../crates/reset-kernel-lib/src/reset.nr | 1 + .../src/reset/transient_data.nr | 280 ++++++++++++++++++ .../crates/reset-kernel-lib/src/tests.nr | 1 + ...non_existent_read_request_hints_builder.nr | 15 +- .../src/tests/squash_transient_data.nr | 31 ++ .../rollup-lib/src/base/base_rollup_inputs.nr | 7 +- .../crates/types/src/abis.nr | 2 + .../combined_accumulated_data.nr | 34 ++- .../private_accumulated_data.nr | 11 +- .../private_accumulated_data_builder.nr | 65 ++-- .../public_accumulated_data.nr | 14 +- .../public_accumulated_data_builder.nr | 12 +- .../crates/types/src/abis/note_hash.nr | 128 ++++++++ .../crates/types/src/abis/nullifier.nr | 59 ++++ .../src/abis/private_circuit_public_inputs.nr | 16 +- .../types/src/abis/public_call_stack_item.nr | 6 +- .../src/abis/public_circuit_public_inputs.nr | 16 +- .../crates/types/src/abis/side_effect.nr | 66 +---- .../crates/types/src/constants.nr | 8 +- .../crates/types/src/mocked.nr | 6 +- .../crates/types/src/tests/fixture_builder.nr | 45 ++- .../private_circuit_public_inputs_builder.nr | 7 +- .../public_circuit_public_inputs_builder.nr | 10 +- yarn-project/circuit-types/src/mocks.ts | 4 +- .../src/hash/__snapshots__/hash.test.ts.snap | 4 - .../circuits.js/src/hash/hash.test.ts | 17 +- yarn-project/circuits.js/src/hash/hash.ts | 10 +- ...r_non_existent_read_request_hints.test.ts} | 121 +------- ...lifier_non_existent_read_request_hints.ts} | 46 +-- ...build_nullifier_read_request_hints.test.ts | 112 +++++++ .../build_nullifier_read_request_hints.ts | 54 ++++ .../hints/build_transient_data_hints.test.ts | 38 +++ .../src/hints/build_transient_data_hints.ts | 44 +++ yarn-project/circuits.js/src/hints/index.ts | 4 +- .../private_call_stack_item.test.ts.snap | 2 +- ...private_circuit_public_inputs.test.ts.snap | 2 +- .../public_call_stack_item.test.ts.snap | 2 +- .../public_circuit_public_inputs.test.ts.snap | 2 +- yarn-project/circuits.js/src/structs/index.ts | 2 + .../kernel/private_accumulated_data.ts | 16 +- ...vate_kernel_init_circuit_private_inputs.ts | 10 +- ...ate_kernel_inner_circuit_private_inputs.ts | 20 +- ...vate_kernel_tail_circuit_private_inputs.ts | 145 ++++++--- ...ivate_kernel_tail_circuit_public_inputs.ts | 17 +- .../structs/kernel/public_accumulated_data.ts | 16 +- .../non_existent_read_request_hints.ts | 18 +- .../circuits.js/src/structs/note_hash.ts | 72 +++++ .../circuits.js/src/structs/nullifier.ts | 38 +++ .../structs/private_circuit_public_inputs.ts | 24 +- .../structs/public_call_stack_item.test.ts | 7 +- .../structs/public_circuit_public_inputs.ts | 40 +-- .../circuits.js/src/structs/side_effects.ts | 98 ------ .../circuits.js/src/tests/factories.ts | 111 ++----- yarn-project/circuits.js/src/utils/index.ts | 42 ++- .../circuits.js/src/utils/utils.test.ts | 161 +++++++++- .../src/type_conversion.ts | 189 +++++++----- .../pxe/src/kernel_prover/hints_builder.ts | 155 ---------- .../src/kernel_prover/kernel_prover.test.ts | 14 +- .../pxe/src/kernel_prover/kernel_prover.ts | 93 ++---- .../build_private_kernel_inner_hints.ts | 17 ++ .../build_private_kernel_tail_hints.ts | 194 ++++++++++++ .../build_private_kernel_tail_outputs.ts | 30 ++ .../private_inputs_builders/index.ts | 3 + .../simulator/src/acvm/oracle/oracle.ts | 10 +- .../simulator/src/acvm/oracle/typed_oracle.ts | 4 +- .../src/client/client_execution_context.ts | 45 ++- .../src/client/execution_note_cache.ts | 23 +- .../src/client/execution_result.test.ts | 1 + .../simulator/src/client/execution_result.ts | 14 +- .../src/client/private_execution.test.ts | 74 ++--- .../simulator/src/client/private_execution.ts | 2 + .../src/public/abstract_phase_manager.ts | 7 +- .../simulator/src/public/execution.ts | 11 +- .../simulator/src/public/hints_builder.ts | 6 +- .../src/public/tail_phase_manager.ts | 20 +- .../src/public/transitional_adaptors.ts | 15 +- .../simulator/src/public/utils.test.ts | 6 +- yarn-project/simulator/src/public/utils.ts | 3 +- 97 files changed, 2746 insertions(+), 1653 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/squash_transient_data.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr rename yarn-project/circuits.js/src/hints/{build_hints.test.ts => build_nullifier_non_existent_read_request_hints.test.ts} (51%) rename yarn-project/circuits.js/src/hints/{build_hints.ts => build_nullifier_non_existent_read_request_hints.ts} (62%) create mode 100644 yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.test.ts create mode 100644 yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts create mode 100644 yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts create mode 100644 yarn-project/circuits.js/src/hints/build_transient_data_hints.ts create mode 100644 yarn-project/circuits.js/src/structs/note_hash.ts create mode 100644 yarn-project/circuits.js/src/structs/nullifier.ts delete mode 100644 yarn-project/pxe/src/kernel_prover/hints_builder.ts create mode 100644 yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.ts create mode 100644 yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts create mode 100644 yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts create mode 100644 yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 184849fff95..40fe7613575 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -16,7 +16,7 @@ use dep::protocol_types::{ private_circuit_public_inputs::PrivateCircuitPublicInputs, public_call_stack_item::PublicCallStackItem, public_circuit_public_inputs::PublicCircuitPublicInputs, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::NoteHash, nullifier::Nullifier, side_effect::SideEffect }, address::{AztecAddress, EthAddress}, constants::{ @@ -53,8 +53,8 @@ struct PrivateContext { nullifier_read_requests: BoundedVec, nullifier_key_validation_requests: BoundedVec, - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, private_call_stack_hashes : BoundedVec, public_call_stack_hashes : BoundedVec, @@ -99,14 +99,12 @@ impl ContextInterface for PrivateContext { } fn push_new_note_hash(&mut self, note_hash: Field) { - let side_effect = SideEffect { value: note_hash, counter: self.side_effect_counter }; - self.new_note_hashes.push(side_effect); + self.new_note_hashes.push(NoteHash { value: note_hash, counter: self.side_effect_counter }); self.side_effect_counter = self.side_effect_counter + 1; } - fn push_new_nullifier(&mut self, nullifier: Field, nullified_commitment: Field) { - let side_effect = SideEffectLinkedToNoteHash { value: nullifier, note_hash: nullified_commitment, counter: self.side_effect_counter }; - self.new_nullifiers.push(side_effect); + fn push_new_nullifier(&mut self, nullifier: Field, nullified_note_hash: Field) { + self.new_nullifiers.push(Nullifier { value: nullifier, note_hash: nullified_note_hash, counter: self.side_effect_counter }); self.side_effect_counter = self.side_effect_counter + 1; } } @@ -492,8 +490,8 @@ impl PrivateContext { contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL], contract_storage_reads: [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL], public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], - new_note_hashes: [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], - new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL], + new_note_hashes: [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], + new_nullifiers: [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL], new_l2_to_l1_msgs: [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL], start_side_effect_counter: 0, end_side_effect_counter: 0, diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index be5c22cc7be..4e39f85944d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -13,7 +13,7 @@ use dep::protocol_types::{ private_circuit_public_inputs::PrivateCircuitPublicInputs, public_call_stack_item::PublicCallStackItem, public_circuit_public_inputs::PublicCircuitPublicInputs, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::NoteHash, nullifier::Nullifier, side_effect::SideEffect }, hash::silo_nullifier, address::{AztecAddress, EthAddress}, constants::{ @@ -40,8 +40,8 @@ struct PublicContext { contract_storage_reads: BoundedVec, public_call_stack_hashes: BoundedVec, - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, new_l2_to_l1_msgs: BoundedVec, @@ -204,18 +204,16 @@ impl ContextInterface for PublicContext { } fn push_new_note_hash(&mut self, note_hash: Field) { - let side_effect = SideEffect { value: note_hash, counter: self.side_effect_counter }; - self.new_note_hashes.push(side_effect); + self.new_note_hashes.push(NoteHash { value: note_hash, counter: self.side_effect_counter }); self.side_effect_counter = self.side_effect_counter + 1; } - fn push_new_nullifier(&mut self, nullifier: Field, _nullified_commitment: Field) { - let side_effect = SideEffectLinkedToNoteHash { + fn push_new_nullifier(&mut self, nullifier: Field, _nullified_note_hash: Field) { + self.new_nullifiers.push(Nullifier { value: nullifier, note_hash: 0, // cannot nullify pending notes in public context counter: self.side_effect_counter - }; - self.new_nullifiers.push(side_effect); + }); self.side_effect_counter = self.side_effect_counter + 1; } } diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index 2991d385290..537416c1068 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -21,12 +21,14 @@ 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); + let note_hash_counter = context.side_effect_counter; assert( notify_created_note( storage_slot, Note::get_note_type_id(), serialized_note, - inner_note_hash + inner_note_hash, + note_hash_counter ) == 0 ); @@ -69,7 +71,9 @@ pub fn destroy_note(context: &mut PrivateContext, note: Note) where Not // TODO(1718): Can we reuse the note hash computed in `compute_nullifier`? consumed_note_hash = compute_note_hash_for_consumption(note); } - assert(notify_nullified_note(nullifier, consumed_note_hash) == 0); + + let nullifier_counter = context.side_effect_counter; + assert(notify_nullified_note(nullifier, consumed_note_hash, nullifier_counter) == 0); context.push_new_nullifier(nullifier, consumed_note_hash) } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 18c3508e4d5..da3b3af393a 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -7,23 +7,39 @@ fn notify_created_note_oracle( _storage_slot: Field, _note_type_id: Field, _serialized_note: [Field; N], - _inner_note_hash: Field + _inner_note_hash: Field, + _counter: u32 ) -> Field {} unconstrained pub fn notify_created_note( storage_slot: Field, note_type_id: Field, serialized_note: [Field; N], - inner_note_hash: Field + inner_note_hash: Field, + counter: u32 ) -> Field { - notify_created_note_oracle(storage_slot, note_type_id, serialized_note, inner_note_hash) + notify_created_note_oracle( + storage_slot, + note_type_id, + serialized_note, + inner_note_hash, + counter + ) } #[oracle(notifyNullifiedNote)] -fn notify_nullified_note_oracle(_nullifier: Field, _inner_note_hash: Field) -> Field {} +fn notify_nullified_note_oracle( + _nullifier: Field, + _inner_note_hash: Field, + _counter: u32 +) -> Field {} -unconstrained pub fn notify_nullified_note(nullifier: Field, inner_note_hash: Field) -> Field { - notify_nullified_note_oracle(nullifier, inner_note_hash) +unconstrained pub fn notify_nullified_note( + nullifier: Field, + inner_note_hash: Field, + counter: u32 +) -> Field { + notify_nullified_note_oracle(nullifier, inner_note_hash, counter) } #[oracle(getNotes)] diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr index c48e9b3708f..aa8f767bf6a 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr @@ -1,11 +1,7 @@ -use dep::protocol_types::{ - constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - abis::side_effect::{SideEffect, SideEffectLinkedToNoteHash} -}; +use dep::protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, abis::side_effect::SideEffect}; use crate::context::{PrivateContext, PublicContext, Context}; use crate::note::{ - constants::MAX_NOTES_PER_PAGE, - lifecycle::{create_note, create_note_hash_from_public, destroy_note}, + constants::MAX_NOTES_PER_PAGE, lifecycle::{create_note, create_note_hash_from_public, destroy_note}, note_getter::{get_notes, view_notes}, note_getter_options::NoteGetterOptions, note_header::NoteHeader, note_interface::NoteInterface, note_viewer_options::NoteViewerOptions, utils::compute_note_hash_for_consumption diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/common.nr index 72c62b8f903..785b41d9f30 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/common.nr @@ -2,29 +2,18 @@ use dep::std; use dep::types::{ abis::{ call_request::CallRequest, accumulated_data::PrivateAccumulatedData, - kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputsBuilder, - max_block_number::MaxBlockNumber, membership_witness::NoteHashReadRequestMembershipWitness, + membership_witness::NoteHashReadRequestMembershipWitness, private_circuit_public_inputs::PrivateCircuitPublicInputs, - private_kernel::private_call_data::PrivateCallData, kernel_data::PrivateKernelData, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + private_kernel::private_call_data::PrivateCallData, side_effect::SideEffect }, - address::{AztecAddress, EthAddress, PartialAddress, compute_initialization_hash}, - contract_class_id::ContractClassId, - constants::{ - MAX_NEW_NULLIFIERS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL -}, - hash::{ - compute_l2_to_l1_hash, pedersen_hash, private_functions_root_from_siblings, silo_note_hash, - silo_nullifier, stdlib_recursion_verification_key_compress_native_vk -}, - merkle_tree::check_membership, - utils::{arrays::{array_length, array_to_bounded_vec, validate_array}}, + address::{AztecAddress, PartialAddress}, contract_class_id::ContractClassId, + constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + hash::{private_functions_root_from_siblings, stdlib_recursion_verification_key_compress_native_vk}, + merkle_tree::check_membership, utils::{arrays::{array_length, validate_array}}, traits::{is_empty, is_empty_array} }; -pub fn validate_arrays(app_public_inputs: PrivateCircuitPublicInputs) { +fn validate_arrays(app_public_inputs: PrivateCircuitPublicInputs) { // Each of the following arrays is expected to be zero-padded. // In addition, some of the following arrays (new_note_hashes, etc...) are passed // to extend_from_array_to_array() routines which rely on the passed arrays to be well-formed. @@ -83,37 +72,6 @@ pub fn validate_note_hash_read_requests( } } -pub fn initialize_end_values( - previous_kernel: PrivateKernelData, - public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder -) { - public_inputs.constants = previous_kernel.public_inputs.constants; - public_inputs.min_revertible_side_effect_counter = previous_kernel.public_inputs.min_revertible_side_effect_counter; - - let start = previous_kernel.public_inputs.validation_requests; - public_inputs.validation_requests.max_block_number = start.for_rollup.max_block_number; - public_inputs.validation_requests.note_hash_read_requests = array_to_bounded_vec(start.note_hash_read_requests); - public_inputs.validation_requests.nullifier_read_requests = array_to_bounded_vec(start.nullifier_read_requests); - public_inputs.validation_requests.nullifier_key_validation_requests = array_to_bounded_vec(start.nullifier_key_validation_requests); - - // Ensure the arrays are the same as previously, before we start pushing more data onto them in other - // functions within this circuit: - let start = previous_kernel.public_inputs.end; - - public_inputs.end.new_note_hashes = array_to_bounded_vec(start.new_note_hashes); - public_inputs.end.new_nullifiers = array_to_bounded_vec(start.new_nullifiers); - - public_inputs.end.private_call_stack = array_to_bounded_vec(start.private_call_stack); - public_inputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); - public_inputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(start.new_l2_to_l1_msgs); - - public_inputs.end.encrypted_logs_hashes = array_to_bounded_vec(start.encrypted_logs_hashes); - public_inputs.end.unencrypted_logs_hashes = array_to_bounded_vec(start.unencrypted_logs_hashes); - - public_inputs.end.encrypted_log_preimages_length = start.encrypted_log_preimages_length; - public_inputs.end.unencrypted_log_preimages_length = start.unencrypted_log_preimages_length; -} - fn perform_static_call_checks(private_call: PrivateCallData) { let public_inputs = private_call.call_stack_item.public_inputs; let is_static_call = public_inputs.call_context.is_static_call; @@ -151,158 +109,45 @@ fn is_valid_caller(request_from_stack: CallRequest, fn_being_verified: PrivateCa & (request_from_stack.caller_context.is_empty() | valid_caller_context) } -fn validate_call_requests( - call_requests: BoundedVec, - hashes: [Field; N], - private_call: PrivateCallData -) { - assert_eq( - array_length(hashes), call_requests.len(), "call requests length does not match the expected length" - ); +fn validate_call_requests(call_requests: [CallRequest; N], hashes: [Field; N], private_call: PrivateCallData) { for i in 0..N { let hash = hashes[i]; + let request = call_requests[i]; if hash != 0 { - let request = call_requests.get_unchecked(i); assert_eq(request.hash, hash, "call stack hash does not match call request hash"); assert(is_valid_caller(request, private_call), "invalid caller"); + } else { + assert(is_empty(request), "call requests length does not match the expected length"); } } } -pub fn update_end_values( - private_call: PrivateCallData, - public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder -) { - // If this call is a static call, certain operations are disallowed, such as creating new state. - perform_static_call_checks(private_call); - +// TODO: Move to a seperate file. +pub fn validate_private_call_data(private_call: PrivateCallData) { let private_call_public_inputs = private_call.call_stack_item.public_inputs; - let storage_contract_address = private_call_public_inputs.call_context.storage_contract_address; + validate_arrays(private_call_public_inputs); - // Update the max block number if the private call requested a lower one. - public_inputs.validation_requests.max_block_number = MaxBlockNumber::min(public_inputs.validation_requests.max_block_number, private_call_public_inputs.max_block_number); + contract_logic(private_call); - // Transient read requests and witnesses are accumulated in public_inputs.end - // We silo the read requests (domain separation per contract address) - let read_requests = private_call_public_inputs.note_hash_read_requests; - let read_request_membership_witnesses = private_call.note_hash_read_request_membership_witnesses; - let mut siloed_read_requests: BoundedVec = BoundedVec::new(); - for i in 0..MAX_NOTE_HASH_READ_REQUESTS_PER_CALL { - let read_request = read_requests[i].value; - let witness = read_request_membership_witnesses[i]; - if witness.is_transient & (read_request != 0) { // only forward transient to public inputs - siloed_read_requests.push( - SideEffect { counter: read_requests[i].counter, value: silo_note_hash(storage_contract_address, read_request) } - ) - } - } - public_inputs.validation_requests.note_hash_read_requests.extend_from_bounded_vec(siloed_read_requests); - - // Nullifier read request. - let nullifier_read_requests = private_call_public_inputs.nullifier_read_requests; - for i in 0..MAX_NULLIFIER_READ_REQUESTS_PER_CALL { - let request = nullifier_read_requests[i]; - if !is_empty(request) { - public_inputs.validation_requests.nullifier_read_requests.push(request.to_context(storage_contract_address)); - } - } - - // Nullifier key validation requests. - let nullifier_key_validation_requests = private_call_public_inputs.nullifier_key_validation_requests; - for i in 0..MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL { - let request = nullifier_key_validation_requests[i]; - if !is_empty(request) { - public_inputs.validation_requests.nullifier_key_validation_requests.push(request.to_context(storage_contract_address)); - } - } - - // Enhance commitments and nullifiers with domain separation whereby domain is the contract. - // - // nullifiers - let new_nullifiers = private_call_public_inputs.new_nullifiers; - let mut siloed_new_nullifiers: BoundedVec = BoundedVec::new(); - for i in 0..MAX_NEW_NULLIFIERS_PER_CALL { - let new_nullifier = new_nullifiers[i]; - if new_nullifier.value != 0 { - let siloed_note_hash = if new_nullifier.note_hash == 0 { - 0 - } else { - silo_note_hash(storage_contract_address, new_nullifier.note_hash) - }; - siloed_new_nullifiers.push( - SideEffectLinkedToNoteHash { - value: silo_nullifier(storage_contract_address, new_nullifier.value), - counter: new_nullifier.counter, - note_hash: siloed_note_hash - } - ); - } - } - public_inputs.end.new_nullifiers.extend_from_bounded_vec(siloed_new_nullifiers); - - // note hashes - let new_note_hashes = private_call_public_inputs.new_note_hashes; - let mut siloed_new_note_hashes: BoundedVec = BoundedVec::new(); - for i in 0..MAX_NEW_NOTE_HASHES_PER_CALL { - let new_note_hash = new_note_hashes[i].value; - if new_note_hash != 0 { - siloed_new_note_hashes.push( - SideEffect { value: silo_note_hash(storage_contract_address, new_note_hash), counter: new_note_hashes[i].counter } - ); - } - } - public_inputs.end.new_note_hashes.extend_from_bounded_vec(siloed_new_note_hashes); + perform_static_call_checks(private_call); - // Call stacks // Private call stack. - let private_call_stack = array_to_bounded_vec(private_call.private_call_stack); validate_call_requests( - private_call_stack, + private_call.private_call_stack, private_call_public_inputs.private_call_stack_hashes, private_call ); - public_inputs.end.private_call_stack.extend_from_bounded_vec(private_call_stack); + // Public call stack. - let public_call_stack = array_to_bounded_vec(private_call.public_call_stack); validate_call_requests( - public_call_stack, + private_call.public_call_stack, private_call_public_inputs.public_call_stack_hashes, private_call ); - public_inputs.end.public_call_stack.extend_from_bounded_vec(public_call_stack); - - // new l2 to l1 messages - let new_l2_to_l1_msgs = private_call_public_inputs.new_l2_to_l1_msgs; - let mut new_l2_to_l1_msgs_to_insert : BoundedVec = BoundedVec::new(); - for i in 0..MAX_NEW_L2_TO_L1_MSGS_PER_CALL { - let msg = new_l2_to_l1_msgs[i]; - if !is_empty(msg) { - let new_l2_to_l1_msgs = compute_l2_to_l1_hash( - storage_contract_address, - private_call_public_inputs.tx_context.version, - private_call_public_inputs.tx_context.chain_id, - msg - ); - new_l2_to_l1_msgs_to_insert.push(new_l2_to_l1_msgs) - } - } - public_inputs.end.new_l2_to_l1_msgs.extend_from_bounded_vec(new_l2_to_l1_msgs_to_insert); - - // logs hashes - // See the following thread if not clear: - // https://discourse.aztec.network/t/proposal-forcing-the-sequencer-to-actually-submit-data-to-l1/426 - - public_inputs.end.encrypted_logs_hashes.extend_from_bounded_vec(array_to_bounded_vec(private_call_public_inputs.encrypted_logs_hashes)); - public_inputs.end.unencrypted_logs_hashes.extend_from_bounded_vec(array_to_bounded_vec(private_call_public_inputs.unencrypted_logs_hashes)); - - // Add log preimages lengths from current iteration to accumulated lengths - public_inputs.end.encrypted_log_preimages_length = public_inputs.end.encrypted_log_preimages_length + - private_call_public_inputs.encrypted_log_preimages_length; - public_inputs.end.unencrypted_log_preimages_length = public_inputs.end.unencrypted_log_preimages_length + private_call_public_inputs.unencrypted_log_preimages_length; } -pub fn contract_logic(private_call: PrivateCallData) { +fn contract_logic(private_call: PrivateCallData) { let contract_address = private_call.call_stack_item.contract_address; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3062): Why is this using a hash function from the stdlib::recursion namespace diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr index 689393b9bbf..9107e0a3429 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr @@ -1,11 +1,14 @@ use crate::common; use dep::std::{cmp::Eq, option::Option}; -use dep::reset_kernel_lib::{NullifierReadRequestHints, PrivateValidationRequestProcessor}; +use dep::reset_kernel_lib::{ + NullifierReadRequestHints, PrivateValidationRequestProcessor, + verify_squashed_transient_note_hashes_and_nullifiers +}; use dep::types::{ abis::{ kernel_data::PrivateKernelData, kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder, PublicKernelCircuitPublicInputs}, - side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered} + note_hash::NoteHashContext, nullifier::Nullifier, side_effect::{SideEffect, Ordered} }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, @@ -28,11 +31,16 @@ fn asc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { struct KernelCircuitPublicInputsComposer { public_inputs: PrivateKernelCircuitPublicInputsBuilder, previous_kernel: PrivateKernelData, - sorted_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], + // Final data + note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], + nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], + // Hints + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], + sorted_note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + sorted_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], sorted_nullifiers_indexes: [u64; MAX_NEW_NULLIFIERS_PER_TX], - transient_note_hash_index_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], sorted_encrypted_log_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX], sorted_encrypted_log_hashes_indexes: [u64; MAX_ENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes: [SideEffect; MAX_UNENCRYPTED_LOGS_PER_TX], @@ -42,11 +50,14 @@ struct KernelCircuitPublicInputsComposer { impl KernelCircuitPublicInputsComposer { pub fn new( previous_kernel: PrivateKernelData, - sorted_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], + note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], + nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], + sorted_note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + sorted_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], sorted_nullifiers_indexes: [u64; MAX_NEW_NULLIFIERS_PER_TX], - transient_note_hash_index_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], sorted_encrypted_log_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX], sorted_encrypted_log_hashes_indexes: [u64; MAX_ENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes: [SideEffect; MAX_UNENCRYPTED_LOGS_PER_TX], @@ -57,11 +68,14 @@ impl KernelCircuitPublicInputsComposer { KernelCircuitPublicInputsComposer { public_inputs, previous_kernel, + note_hashes, + nullifiers, + transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers, sorted_note_hashes, sorted_note_hashes_indexes, sorted_nullifiers, sorted_nullifiers_indexes, - transient_note_hash_index_hints, sorted_encrypted_log_hashes, sorted_encrypted_log_hashes_indexes, sorted_unencrypted_log_hashes, @@ -81,7 +95,7 @@ impl KernelCircuitPublicInputsComposer { self.propagate_sorted_arrays(); // TODO: Should be done in a reset circuit. - self.squash_transient_note_hashes_and_nullifiers(); + self.squash_transient_data(); self.silo_values(); @@ -186,62 +200,26 @@ impl KernelCircuitPublicInputsComposer { self.public_inputs.end.public_call_stack = array_to_bounded_vec(accumulated_data.public_call_stack); } - fn squash_transient_note_hashes_and_nullifiers(&mut self) { - // Remark: The commitments in public_inputs.end have already been siloed by contract address! - // Match nullifiers/nullified_commitments to commitments from the previous call(s) - let mut new_note_hashes = self.public_inputs.end.new_note_hashes.storage; - let mut new_nullifiers = self.public_inputs.end.new_nullifiers.storage; - - // Should start from 1 to skip the first nullifier? - for n_idx in 0..MAX_NEW_NULLIFIERS_PER_TX { - let nullifier = new_nullifiers[n_idx]; - // TODO - should not be able to squash the first nullifier. - let nullified_note_hash = nullifier.note_hash; - let hint_pos = self.transient_note_hash_index_hints[n_idx]; - - // Nullified_commitment of value `0` implies non-transient (persistable) - // nullifier in which case no attempt will be made to match it to a hash. - // Non-empty nullified_note_hash implies transient nullifier which MUST be matched to a hash below! - // 0-valued nullified_note_hash is empty and will be ignored - if nullified_note_hash != 0 { - assert( - hint_pos < MAX_NEW_NOTE_HASHES_PER_TX, "New nullifier is transient but hint is invalid" - ); - let hash = new_note_hashes[hint_pos]; - assert_eq(nullified_note_hash, hash.value, "Hinted hash does not match"); - assert( - nullifier.counter > hash.counter, "Nullifier counter must be greater than hash counter" - ); - // match found! - // squash both the nullifier and the hash - // (set to 0 here and then rearrange array after loop) - new_note_hashes[hint_pos] = SideEffect::empty(); - new_nullifiers[n_idx] = SideEffectLinkedToNoteHash::empty(); - } - // non-transient (persistable) nullifiers are just kept in new_nullifiers array and forwarded - // to public inputs (used later by base rollup circuit) - } - - // Move all zero-ed (removed) entries of these arrays to the end and preserve ordering of other entries - - let mut new_note_hashes_vec = BoundedVec::new(); + fn squash_transient_data(&mut self) { + verify_squashed_transient_note_hashes_and_nullifiers( + self.public_inputs.end.new_note_hashes.storage, + self.public_inputs.end.new_nullifiers.storage, + self.note_hashes, + self.nullifiers, + self.transient_nullifier_indexes_for_note_hashes, + self.transient_note_hash_indexes_for_nullifiers + ); - for c_idx in 0..MAX_NEW_NOTE_HASHES_PER_TX { - if new_note_hashes[c_idx].value != 0 { - new_note_hashes_vec.push(new_note_hashes[c_idx]); - } + // Currently all the transient note hashes and nullifiers must be cleared in the tail circuits. + // Check that the propagated note hashes don't link to a nullifier, and vice versa. + for i in 0..self.note_hashes.len() { + assert(self.note_hashes[i].nullifier_counter == 0, "Unresolved transient note hash"); } - - self.public_inputs.end.new_note_hashes = new_note_hashes_vec; - - let mut new_nullifiers_vec = BoundedVec::new(); - - for n_idx in 0..MAX_NEW_NULLIFIERS_PER_TX { - if new_nullifiers[n_idx].value != 0 { - new_nullifiers_vec.push(new_nullifiers[n_idx]); - } + for i in 0..self.nullifiers.len() { + assert(self.nullifiers[i].note_hash == 0, "Unresolved transient nullifier"); } - self.public_inputs.end.new_nullifiers = new_nullifiers_vec; + self.public_inputs.end.new_note_hashes = array_to_bounded_vec(self.note_hashes); + self.public_inputs.end.new_nullifiers = array_to_bounded_vec(self.nullifiers); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr index d4d12a8b06a..7ce19ad3c8e 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr @@ -1,4 +1,5 @@ mod kernel_circuit_public_inputs_composer; +mod private_kernel_circuit_public_inputs_composer; mod private_kernel_init; mod private_kernel_inner; mod private_kernel_tail; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr new file mode 100644 index 00000000000..a47adc3673d --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr @@ -0,0 +1,240 @@ +use dep::types::{ + abis::{ + call_request::CallRequest, combined_constant_data::CombinedConstantData, + kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder}, + max_block_number::MaxBlockNumber, membership_witness::NoteHashReadRequestMembershipWitness, + nullifier::Nullifier, private_circuit_public_inputs::PrivateCircuitPublicInputs, + side_effect::SideEffect +}, + address::AztecAddress, + constants::{ + MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL +}, + hash::{compute_l2_to_l1_hash, silo_note_hash, silo_nullifier}, traits::is_empty, + transaction::tx_request::TxRequest, utils::arrays::array_to_bounded_vec +}; + +struct DataSource { + private_call_public_inputs: PrivateCircuitPublicInputs, + storage_contract_address: AztecAddress, + note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL], + note_hash_read_request_membership_witnesses: [NoteHashReadRequestMembershipWitness; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], + private_call_requests: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL], + public_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], +} + +struct PrivateKernelCircuitPublicInputsComposer { + public_inputs: PrivateKernelCircuitPublicInputsBuilder, +} + +impl PrivateKernelCircuitPublicInputsComposer { + pub fn new_from_tx_request(tx_request: TxRequest, private_call_public_inputs: PrivateCircuitPublicInputs) -> Self { + let mut public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty(); + + public_inputs.constants = CombinedConstantData::private( + private_call_public_inputs.historical_header, + tx_request.tx_context, + ); + + public_inputs.min_revertible_side_effect_counter = private_call_public_inputs.min_revertible_side_effect_counter; + + // Since it's the first iteration, we need to push the the tx hash nullifier into the `new_nullifiers` array + public_inputs.end.new_nullifiers.push(Nullifier { value: tx_request.hash(), note_hash: 0, counter: 0 }); + // Note that we do not need to nullify the transaction request nonce anymore. + // Should an account want to additionally use nonces for replay protection or handling cancellations, + // they will be able to do so in the account contract logic: + // https://github.com/AztecProtocol/aztec-packages/issues/660 + + PrivateKernelCircuitPublicInputsComposer { public_inputs } + } + + pub fn new_from_previous_kernel(previous_kernel_public_inputs: PrivateKernelCircuitPublicInputs) -> Self { + let mut public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty(); + + public_inputs.constants = previous_kernel_public_inputs.constants; + public_inputs.min_revertible_side_effect_counter = previous_kernel_public_inputs.min_revertible_side_effect_counter; + + let start = previous_kernel_public_inputs.validation_requests; + public_inputs.validation_requests.max_block_number = start.for_rollup.max_block_number; + public_inputs.validation_requests.note_hash_read_requests = array_to_bounded_vec(start.note_hash_read_requests); + public_inputs.validation_requests.nullifier_read_requests = array_to_bounded_vec(start.nullifier_read_requests); + public_inputs.validation_requests.nullifier_key_validation_requests = array_to_bounded_vec(start.nullifier_key_validation_requests); + + let start = previous_kernel_public_inputs.end; + public_inputs.end.new_note_hashes = array_to_bounded_vec(start.new_note_hashes); + public_inputs.end.new_nullifiers = array_to_bounded_vec(start.new_nullifiers); + public_inputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(start.new_l2_to_l1_msgs); + public_inputs.end.encrypted_logs_hashes = array_to_bounded_vec(start.encrypted_logs_hashes); + public_inputs.end.unencrypted_logs_hashes = array_to_bounded_vec(start.unencrypted_logs_hashes); + public_inputs.end.encrypted_log_preimages_length = start.encrypted_log_preimages_length; + public_inputs.end.unencrypted_log_preimages_length = start.unencrypted_log_preimages_length; + public_inputs.end.private_call_stack = array_to_bounded_vec(start.private_call_stack); + let _call_request = public_inputs.end.private_call_stack.pop(); + public_inputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); + + PrivateKernelCircuitPublicInputsComposer { public_inputs } + } + + pub fn compose( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL], + note_hash_read_request_membership_witnesses: [NoteHashReadRequestMembershipWitness; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], + private_call_requests: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL], + public_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL] + ) -> Self { + let storage_contract_address = private_call_public_inputs.call_context.storage_contract_address; + let source = DataSource { + storage_contract_address, + private_call_public_inputs, + note_hash_nullifier_counters, + note_hash_read_request_membership_witnesses, + private_call_requests, + public_call_requests + }; + + self.propagate_max_block_number(source); + self.propagate_note_hash_read_requests(source); + self.propagate_nullifier_read_requests(source); + self.propagate_nullifier_key_validation_requests(source); + self.propagate_note_hashes(source); + self.propagate_nullifiers(source); + self.propagate_l2_to_l1_messages(source); + self.propagate_logs(source); + self.propagate_private_call_requests(source); + self.propagate_public_call_requests(source); + + *self + } + + pub fn finish(self) -> PrivateKernelCircuitPublicInputs { + self.public_inputs.finish() + } + + fn propagate_max_block_number(&mut self, source: DataSource) { + // Update the max block number if the private call requested a lower one. + self.public_inputs.validation_requests.max_block_number = MaxBlockNumber::min(self.public_inputs.validation_requests.max_block_number, source.private_call_public_inputs.max_block_number); + } + + fn propagate_note_hash_read_requests(&mut self, source: DataSource) { + // TODO: Propagate all requests to verify in a reset circuit. + // Transient read requests and witnesses are accumulated in public_inputs.end + // We silo the read requests (domain separation per contract address) + let read_requests = source.private_call_public_inputs.note_hash_read_requests; + for i in 0..read_requests.len() { + let read_request = read_requests[i]; + let witness = source.note_hash_read_request_membership_witnesses[i]; + if witness.is_transient & (read_request.value != 0) { // only forward transient to public inputs + let siloed_read_request = SideEffect { + counter: read_request.counter, + value: silo_note_hash(source.storage_contract_address, read_request.value) + }; + self.public_inputs.validation_requests.note_hash_read_requests.push(siloed_read_request); + } + } + } + + fn propagate_nullifier_read_requests(&mut self, source: DataSource) { + let nullifier_read_requests = source.private_call_public_inputs.nullifier_read_requests; + for i in 0..nullifier_read_requests.len() { + let request = nullifier_read_requests[i]; + if !is_empty(request) { + self.public_inputs.validation_requests.nullifier_read_requests.push(request.to_context(source.storage_contract_address)); + } + } + } + + fn propagate_nullifier_key_validation_requests(&mut self, source: DataSource) { + let nullifier_key_validation_requests = source.private_call_public_inputs.nullifier_key_validation_requests; + for i in 0..nullifier_key_validation_requests.len() { + let request = nullifier_key_validation_requests[i]; + if !is_empty(request) { + self.public_inputs.validation_requests.nullifier_key_validation_requests.push(request.to_context(source.storage_contract_address)); + } + } + } + + fn propagate_note_hashes(&mut self, source: DataSource) { + let note_hashes = source.private_call_public_inputs.new_note_hashes; + for i in 0..note_hashes.len() { + let mut note_hash = note_hashes[i]; + if note_hash.value != 0 { + let nullifier_counter = source.note_hash_nullifier_counters[i]; + assert( + (nullifier_counter == 0) | (nullifier_counter > note_hash.counter), "invalid nullifier counter" + ); + + // TODO: Silo values in the tail circuit. + note_hash.value = silo_note_hash(source.storage_contract_address, note_hash.value); + self.public_inputs.end.new_note_hashes.push(note_hash.to_context(nullifier_counter)); + } + } + } + + fn propagate_nullifiers(&mut self, source: DataSource) { + let nullifiers = source.private_call_public_inputs.new_nullifiers; + for i in 0..nullifiers.len() { + let nullifier = nullifiers[i]; + if nullifier.value != 0 { + let siloed_note_hash = if nullifier.note_hash == 0 { + 0 + } else { + silo_note_hash(source.storage_contract_address, nullifier.note_hash) + }; + self.public_inputs.end.new_nullifiers.push( + Nullifier { + value: silo_nullifier(source.storage_contract_address, nullifier.value), + counter: nullifier.counter, + note_hash: siloed_note_hash + } + ); + } + } + } + + fn propagate_l2_to_l1_messages(&mut self, source: DataSource) { + let l2_to_l1_msgs = source.private_call_public_inputs.new_l2_to_l1_msgs; + for i in 0..l2_to_l1_msgs.len() { + let msg = l2_to_l1_msgs[i]; + if !is_empty(msg) { + let hash = compute_l2_to_l1_hash( + source.storage_contract_address, + source.private_call_public_inputs.tx_context.version, + source.private_call_public_inputs.tx_context.chain_id, + msg + ); + self.public_inputs.end.new_l2_to_l1_msgs.push(hash); + } + } + } + + fn propagate_logs(&mut self, source: DataSource) { + self.public_inputs.end.encrypted_logs_hashes.extend_from_bounded_vec(array_to_bounded_vec(source.private_call_public_inputs.encrypted_logs_hashes)); + self.public_inputs.end.unencrypted_logs_hashes.extend_from_bounded_vec(array_to_bounded_vec(source.private_call_public_inputs.unencrypted_logs_hashes)); + + // Add log preimages lengths from current iteration to accumulated lengths. + self.public_inputs.end.encrypted_log_preimages_length += source.private_call_public_inputs.encrypted_log_preimages_length; + self.public_inputs.end.unencrypted_log_preimages_length += source.private_call_public_inputs.unencrypted_log_preimages_length; + } + + fn propagate_private_call_requests(&mut self, source: DataSource) { + let call_requests = source.private_call_requests; + for i in 0..call_requests.len() { + let call_request = call_requests[i]; + if !is_empty(call_request) { + self.public_inputs.end.private_call_stack.push(call_request); + } + } + } + + fn propagate_public_call_requests(&mut self, source: DataSource) { + let call_requests = source.public_call_requests; + for i in 0..call_requests.len() { + let call_request = call_requests[i]; + if !is_empty(call_request) { + self.public_inputs.end.public_call_stack.push(call_request); + } + } + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr index f83df8a71ae..33f32c12a86 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr @@ -1,31 +1,25 @@ -use crate::common; +use crate::{common, private_kernel_circuit_public_inputs_composer::PrivateKernelCircuitPublicInputsComposer}; use dep::types::{ abis::{ - gas_settings::GasSettings, combined_constant_data::CombinedConstantData, private_kernel::private_call_data::PrivateCallData, - kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder}, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs }, - address::{AztecAddress, PublicKeysHash, compute_initialization_hash}, - mocked::{Proof, verify_previous_kernel_state}, transaction::tx_request::TxRequest, - traits::is_empty_array + constants::MAX_NEW_NOTE_HASHES_PER_CALL, mocked::verify_private_function_proof, + transaction::tx_request::TxRequest }; +struct PrivateKernelInitHints { + note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL], +} + // Initialization struct for private inputs to the private kernel struct PrivateKernelInitCircuitPrivateInputs { tx_request: TxRequest, private_call: PrivateCallData, + hints: PrivateKernelInitHints, } impl PrivateKernelInitCircuitPrivateInputs { - fn initialize_end_values(self, public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder) { - public_inputs.constants = CombinedConstantData::private( - self.private_call.call_stack_item.public_inputs.historical_header, - self.tx_request.tx_context, - ); - public_inputs.min_revertible_side_effect_counter = self.private_call.call_stack_item.public_inputs.min_revertible_side_effect_counter; - } - // Confirm that the TxRequest (user's intent) // matches the private call being executed fn validate_this_private_call_against_tx_request(self) { @@ -71,61 +65,46 @@ impl PrivateKernelInitCircuitPrivateInputs { ); } - fn update_end_values(self, public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder) { - // Since it's the first iteration, we need to push the the tx hash nullifier into the `new_nullifiers` array - public_inputs.end.new_nullifiers.push(SideEffectLinkedToNoteHash { value: self.tx_request.hash(), note_hash: 0, counter: 0 }); - // Note that we do not need to nullify the transaction request nonce anymore. - // Should an account want to additionally use nonces for replay protection or handling cancellations, - // they will be able to do so in the account contract logic: - // https://github.com/AztecProtocol/aztec-packages/issues/660 - } - pub fn native_private_kernel_circuit_initial(self) -> PrivateKernelCircuitPublicInputs { - let mut public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty(); - - self.initialize_end_values(&mut public_inputs); + let private_call_public_inputs = self.private_call.call_stack_item.public_inputs; self.validate_inputs(); - common::validate_arrays(self.private_call.call_stack_item.public_inputs); + common::validate_private_call_data(self.private_call); self.validate_this_private_call_against_tx_request(); // TODO: Do this in a reset circuit. common::validate_note_hash_read_requests( - public_inputs.constants.historical_header.state.partial.note_hash_tree.root, + private_call_public_inputs.historical_header.state.partial.note_hash_tree.root, self.private_call.call_stack_item.public_inputs.note_hash_read_requests, self.private_call.note_hash_read_request_membership_witnesses ); - // TODO(dbanks12): feels like update_end_values should happen after contract logic - self.update_end_values(&mut public_inputs); - common::update_end_values(self.private_call, &mut public_inputs); - - common::contract_logic(self.private_call); - - // TODO(David): What previous kernel proof are we talking about, since this is private-kernel-init - let (is_previous_state_valid, updated_aggregation_object) = verify_previous_kernel_state(public_inputs.aggregation_object, self.private_call.proof); - assert(is_previous_state_valid); + assert(verify_private_function_proof(self.private_call.proof), "Invalid private function proof."); - public_inputs.aggregation_object = updated_aggregation_object; - - public_inputs.finish() + PrivateKernelCircuitPublicInputsComposer::new_from_tx_request(self.tx_request, private_call_public_inputs).compose( + private_call_public_inputs, + self.hints.note_hash_nullifier_counters, + self.private_call.note_hash_read_request_membership_witnesses, + self.private_call.private_call_stack, + self.private_call.public_call_stack + ).finish() } } mod tests { - use crate::private_kernel_init::PrivateKernelInitCircuitPrivateInputs; + use crate::private_kernel_init::{PrivateKernelInitHints, PrivateKernelInitCircuitPrivateInputs}; use dep::types::{ abis::{ - kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, note_hash::NoteHash, nullifier_key_validation_request::NullifierKeyValidationRequest, private_kernel::private_call_data::PrivateCallData, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + nullifier::Nullifier, side_effect::SideEffect }, - address::{AztecAddress, EthAddress, compute_initialization_hash}, - constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, grumpkin_point::GrumpkinPoint, - grumpkin_private_key::GrumpkinPrivateKey, + address::{AztecAddress, EthAddress}, + constants::{MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL}, + grumpkin_point::GrumpkinPoint, grumpkin_private_key::GrumpkinPrivateKey, hash::stdlib_recursion_verification_key_compress_native_vk, messaging::l2_to_l1_message::L2ToL1Message, tests::private_call_data_builder::PrivateCallDataBuilder, transaction::tx_request::TxRequest, @@ -135,18 +114,20 @@ mod tests { struct PrivateKernelInitInputsBuilder { tx_request: TxRequest, private_call: PrivateCallDataBuilder, + hints: PrivateKernelInitHints, } impl PrivateKernelInitInputsBuilder { pub fn new() -> Self { let private_call = PrivateCallDataBuilder::new(); let tx_request = private_call.build_tx_request(); + let hints = PrivateKernelInitHints { note_hash_nullifier_counters: [0; MAX_NEW_NOTE_HASHES_PER_CALL] }; - PrivateKernelInitInputsBuilder { tx_request, private_call } + PrivateKernelInitInputsBuilder { tx_request, private_call, hints } } pub fn execute(self) -> PrivateKernelCircuitPublicInputs { - let kernel = PrivateKernelInitCircuitPrivateInputs { tx_request: self.tx_request, private_call: self.private_call.finish() }; + let kernel = PrivateKernelInitCircuitPrivateInputs { tx_request: self.tx_request, private_call: self.private_call.finish(), hints: self.hints }; kernel.native_private_kernel_circuit_initial() } @@ -210,13 +191,13 @@ mod tests { } #[test(should_fail_with = "invalid array")] - fn input_validation_malformed_arrays_commitments() { + fn input_validation_malformed_arrays_note_hashes() { let mut builder = PrivateKernelInitInputsBuilder::new(); builder.private_call.public_inputs.new_note_hashes.extend_from_array( [ - SideEffect { value: 0, counter: 0 }, - SideEffect { value: 9123, counter: 1 } + NoteHash { value: 0, counter: 0 }, + NoteHash { value: 9123, counter: 1 } ] ); @@ -229,8 +210,8 @@ mod tests { builder.private_call.public_inputs.new_nullifiers.extend_from_array( [ - SideEffectLinkedToNoteHash { value: 0, note_hash: 0, counter: 0 }, - SideEffectLinkedToNoteHash { value: 9123, note_hash: 0, counter: 1 } + Nullifier { value: 0, note_hash: 0, counter: 0 }, + Nullifier { value: 9123, note_hash: 0, counter: 1 } ] ); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index 03425d36d98..b825e91ad40 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -1,24 +1,25 @@ -use crate::common; +use crate::{common, private_kernel_circuit_public_inputs_composer::PrivateKernelCircuitPublicInputsComposer}; use dep::types::{ abis::{ kernel_data::PrivateKernelData, private_kernel::private_call_data::PrivateCallData, kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder}, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + side_effect::SideEffect }, - mocked::verify_previous_kernel_state + constants::MAX_NEW_NOTE_HASHES_PER_CALL, mocked::verify_previous_kernel_state, + utils::arrays::array_length }; +struct PrivateKernelInnerHints { + note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL], +} + struct PrivateKernelInnerCircuitPrivateInputs { previous_kernel: PrivateKernelData, private_call: PrivateCallData, + hints: PrivateKernelInnerHints, } impl PrivateKernelInnerCircuitPrivateInputs { - fn pop_and_validate_this_private_call_hash(self, public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder) { - let call_request = public_inputs.end.private_call_stack.pop(); - common::validate_call_against_request(self.private_call, call_request); - } - fn validate_inputs(self) { let this_call_stack_item = self.private_call.call_stack_item; let function_data = this_call_stack_item.function_data; @@ -26,49 +27,54 @@ impl PrivateKernelInnerCircuitPrivateInputs { } pub fn native_private_kernel_circuit_inner(self) -> PrivateKernelCircuitPublicInputs { - let mut public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty(); - - common::validate_previous_kernel_values(self.previous_kernel.public_inputs.end); + let private_call_public_inputs = self.private_call.call_stack_item.public_inputs; + let previous_kernel_public_inputs = self.previous_kernel.public_inputs; - // Do this before any functions can modify the inputs. - common::initialize_end_values(self.previous_kernel, &mut public_inputs); + common::validate_previous_kernel_values(previous_kernel_public_inputs.end); self.validate_inputs(); - common::validate_arrays(self.private_call.call_stack_item.public_inputs); + common::validate_private_call_data(self.private_call); - self.pop_and_validate_this_private_call_hash(&mut public_inputs); + let mut private_call_stack = previous_kernel_public_inputs.end.private_call_stack; + // TODO: Should be a hint from private inputs. + let private_call_stack_size = array_length(private_call_stack); + let call_request = private_call_stack[private_call_stack_size - 1]; + common::validate_call_against_request(self.private_call, call_request); // TODO: Do this in a reset circuit. common::validate_note_hash_read_requests( - public_inputs.constants.historical_header.state.partial.note_hash_tree.root, - self.private_call.call_stack_item.public_inputs.note_hash_read_requests, // read requests from private call + previous_kernel_public_inputs.constants.historical_header.state.partial.note_hash_tree.root, + private_call_public_inputs.note_hash_read_requests, // read requests from private call self.private_call.note_hash_read_request_membership_witnesses ); - //TODO(David): feels like update_end_values should happen later - common::update_end_values(self.private_call, &mut public_inputs); - - common::contract_logic(self.private_call); - - let (is_previous_state_valid, updated_aggregation_object) = verify_previous_kernel_state(public_inputs.aggregation_object, self.private_call.proof); + let (is_previous_state_valid, _updated_aggregation_object) = verify_previous_kernel_state( + previous_kernel_public_inputs.aggregation_object, + self.private_call.proof + ); assert(is_previous_state_valid); - public_inputs.aggregation_object = updated_aggregation_object; - - public_inputs.finish() + PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(self.previous_kernel.public_inputs).compose( + private_call_public_inputs, + self.hints.note_hash_nullifier_counters, + self.private_call.note_hash_read_request_membership_witnesses, + self.private_call.private_call_stack, + self.private_call.public_call_stack + ).finish() } } mod tests { - use crate::private_kernel_inner::PrivateKernelInnerCircuitPrivateInputs; + use crate::private_kernel_inner::{PrivateKernelInnerCircuitPrivateInputs, PrivateKernelInnerHints}; use dep::types::constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_TX}; use dep::types::{ abis::{ kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - max_block_number::MaxBlockNumber, side_effect::{SideEffect, SideEffectLinkedToNoteHash} + max_block_number::MaxBlockNumber, note_hash::NoteHash, nullifier::Nullifier, + side_effect::SideEffect }, - address::{AztecAddress, EthAddress}, + address::{AztecAddress, EthAddress}, constants::MAX_NEW_NOTE_HASHES_PER_CALL, messaging::l2_to_l1_message::L2ToL1Message, utils::{arrays::array_length}, tests::{private_call_data_builder::PrivateCallDataBuilder, fixture_builder::FixtureBuilder} }; @@ -76,17 +82,19 @@ mod tests { struct PrivateKernelInnerInputsBuilder { previous_kernel: FixtureBuilder, private_call: PrivateCallDataBuilder, + hints: PrivateKernelInnerHints, } impl PrivateKernelInnerInputsBuilder { pub fn new() -> Self { let mut previous_kernel = FixtureBuilder::new(); let private_call = PrivateCallDataBuilder::new(); + let hints = PrivateKernelInnerHints { note_hash_nullifier_counters: [0; MAX_NEW_NOTE_HASHES_PER_CALL] }; // 0th nullifier must be non-zero. previous_kernel.append_new_nullifiers(1); - PrivateKernelInnerInputsBuilder { previous_kernel, private_call } + PrivateKernelInnerInputsBuilder { previous_kernel, private_call, hints } } pub fn is_delegate_call(&mut self) -> Self { @@ -107,7 +115,7 @@ mod tests { self.previous_kernel.push_private_call_request(hash, is_delegate_call); let previous_kernel = self.previous_kernel.to_private_kernel_data(); - let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call }; + let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call, hints: self.hints }; kernel.native_private_kernel_circuit_inner() } @@ -185,7 +193,7 @@ mod tests { builder.previous_kernel.push_private_call_request(hash + 1, false); let previous_kernel = builder.previous_kernel.to_private_kernel_data(); - let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call }; + let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call, hints: builder.hints }; let _ = kernel.native_private_kernel_circuit_inner(); } @@ -227,7 +235,7 @@ mod tests { builder.previous_kernel.push_private_call_request(hash, is_delegate_call); let previous_kernel = builder.previous_kernel.to_private_kernel_data(); - let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call }; + let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call, hints: builder.hints }; let _ = kernel.native_private_kernel_circuit_inner(); } @@ -256,9 +264,20 @@ mod tests { fn incorrect_storage_contract_for_delegate_calls_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new().is_delegate_call(); - builder.private_call.contract_address = builder.private_call.public_inputs.call_context.storage_contract_address; + // Change the storage contract address to be the same as the contract address. + builder.private_call.public_inputs.call_context.storage_contract_address = builder.private_call.contract_address; + + let private_call = builder.private_call.finish(); + let hash = private_call.call_stack_item.hash(); + builder.previous_kernel.push_private_call_request(hash, true); + let mut call_request = builder.previous_kernel.private_call_stack.pop(); + // Change the caller's storage contract address to be the same as the contract address. + call_request.caller_context.storage_contract_address = builder.private_call.contract_address; + builder.previous_kernel.private_call_stack.push(call_request); - builder.failed(); + let previous_kernel = builder.previous_kernel.to_private_kernel_data(); + let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call, hints: builder.hints }; + let _ = kernel.native_private_kernel_circuit_inner(); } #[test] @@ -278,8 +297,8 @@ mod tests { let mut builder = PrivateKernelInnerInputsBuilder::new(); builder.private_call.append_private_call_requests(2, false); - // Remove one call request. - let _ = builder.private_call.private_call_stack.pop(); + // Remove one call stack item hash. + let _ = builder.private_call.public_inputs.private_call_stack_hashes.pop(); builder.failed(); } @@ -289,8 +308,8 @@ mod tests { let mut builder = PrivateKernelInnerInputsBuilder::new(); builder.private_call.append_public_call_requests(2, false); - // Remove one call request. - let _ = builder.private_call.public_call_stack.pop(); + // Remove one call stack item hash. + let _ = builder.private_call.public_inputs.public_call_stack_hashes.pop(); builder.failed(); } @@ -388,13 +407,13 @@ mod tests { } #[test(should_fail_with = "invalid array")] - fn input_validation_malformed_arrays_commitments() { + fn input_validation_malformed_arrays_note_hashes() { let mut builder = PrivateKernelInnerInputsBuilder::new(); builder.private_call.public_inputs.new_note_hashes.extend_from_array( [ - SideEffect { value: 0, counter: 0 }, - SideEffect { value: 9123, counter: 1 } + NoteHash { value: 0, counter: 0 }, + NoteHash { value: 9123, counter: 1 } ] ); @@ -407,8 +426,8 @@ mod tests { builder.private_call.public_inputs.new_nullifiers.extend_from_array( [ - SideEffectLinkedToNoteHash { value: 0, note_hash: 0, counter: 0 }, - SideEffectLinkedToNoteHash { value: 12, note_hash: 0, counter: 1 } + Nullifier { value: 0, note_hash: 0, counter: 0 }, + Nullifier { value: 12, note_hash: 0, counter: 1 } ] ); @@ -461,19 +480,47 @@ mod tests { builder.failed(); } - #[test(should_fail_with = "extend_from_bounded_vec out of bounds")] - fn private_kernel_should_fail_if_aggregating_too_many_commitments() { + #[test(should_fail_with = "push out of bounds")] + fn private_kernel_should_fail_if_aggregating_too_many_note_hashes() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - // The current call stack has 1 commitment; - builder.private_call.public_inputs.new_note_hashes.push(SideEffect { value: 4321, counter: 0 }); + // The current call stack has 1 note_hash; + builder.private_call.public_inputs.new_note_hashes.push(NoteHash { value: 4321, counter: 0 }); - // Mock the previous new note hashes to be full, therefore no more commitments can be added. + // Mock the previous new note hashes to be full, therefore no more note_hashes can be added. builder.previous_kernel.append_new_note_hashes(MAX_NEW_NOTE_HASHES_PER_TX); builder.failed(); } + #[test] + fn propagate_note_hashes_with_nullifier_counters() { + let mut builder = PrivateKernelInnerInputsBuilder::new(); + let note_hashes = [ + NoteHash { value: 12, counter: 3 }, + NoteHash { value: 45, counter: 6 }, + NoteHash { value: 78, counter: 9 } + ]; + builder.private_call.public_inputs.new_note_hashes.extend_from_array(note_hashes); + builder.hints.note_hash_nullifier_counters[0] = 10; + builder.hints.note_hash_nullifier_counters[2] = 20; + + let public_inputs = builder.execute(); + + assert_eq(public_inputs.end.new_note_hashes[0].nullifier_counter, 10); + assert_eq(public_inputs.end.new_note_hashes[1].nullifier_counter, 0); + assert_eq(public_inputs.end.new_note_hashes[2].nullifier_counter, 20); + } + + #[test(should_fail_with="invalid nullifier counter")] + fn propagate_note_hashes_with_incorrect_nullifier_counters_fails() { + let mut builder = PrivateKernelInnerInputsBuilder::new(); + builder.private_call.public_inputs.new_note_hashes.push(NoteHash { value: 12, counter: 3 }); + builder.hints.note_hash_nullifier_counters[0] = 2; // Less than the note hash's counter 3. + + builder.failed(); + } + #[test(should_fail_with="Private kernel circuit can only execute a private function")] fn private_function_is_private_false_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -697,7 +744,7 @@ mod tests { fn creating_new_note_hashes_on_static_call_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new().is_static_call(); - builder.private_call.public_inputs.new_note_hashes.push(SideEffect { value: 1, counter: 0 }); + builder.private_call.public_inputs.new_note_hashes.push(NoteHash { value: 1, counter: 0 }); builder.failed(); } @@ -706,7 +753,7 @@ mod tests { fn creating_new_nullifiers_on_static_call_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new().is_static_call(); - builder.private_call.public_inputs.new_nullifiers.push(SideEffectLinkedToNoteHash { value: 1, note_hash: 0, counter: 0 }); + builder.private_call.public_inputs.new_nullifiers.push(Nullifier { value: 1, note_hash: 0, counter: 0 }); builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr index 88e8348c6f0..551acfa1983 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr @@ -3,7 +3,7 @@ use dep::reset_kernel_lib::{NullifierReadRequestHints, PrivateValidationRequestP use dep::types::{ abis::{ kernel_data::PrivateKernelData, kernel_circuit_public_inputs::KernelCircuitPublicInputs, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::NoteHashContext, nullifier::Nullifier, side_effect::SideEffect }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, @@ -12,20 +12,32 @@ use dep::types::{ grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length }; -struct PrivateKernelTailCircuitPrivateInputs { - previous_kernel: PrivateKernelData, - sorted_new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], +// Can just be KernelCircuitPublicInputs. +struct PrivateKernelTailOutputs { + note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], + nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], +} + +struct PrivateKernelTailHints { + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], + nullifier_read_request_hints: NullifierReadRequestHints, + master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], + sorted_new_note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], sorted_new_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - read_commitment_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - sorted_new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + sorted_new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], sorted_new_nullifiers_indexes: [u64; MAX_NEW_NULLIFIERS_PER_TX], - nullifier_read_request_hints: NullifierReadRequestHints, - nullifier_commitment_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], sorted_encrypted_log_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX], sorted_encrypted_log_hashes_indexes: [u64; MAX_ENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes: [SideEffect; MAX_UNENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes_indexes: [u64; MAX_UNENCRYPTED_LOGS_PER_TX], - master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], +} + +struct PrivateKernelTailCircuitPrivateInputs { + previous_kernel: PrivateKernelData, + outputs: PrivateKernelTailOutputs, + hints: PrivateKernelTailHints, } impl PrivateKernelTailCircuitPrivateInputs { @@ -38,35 +50,41 @@ impl PrivateKernelTailCircuitPrivateInputs { let nullifier_tree_root = previous_public_inputs.constants.historical_header.state.partial.nullifier_tree.root; let request_processor = PrivateValidationRequestProcessor { validation_requests: previous_public_inputs.validation_requests, - note_hash_read_request_hints: self.read_commitment_hints, + note_hash_read_request_hints: self.hints.note_hash_read_request_hints, pending_note_hashes: previous_public_inputs.end.new_note_hashes, - nullifier_read_request_hints: self.nullifier_read_request_hints, + nullifier_read_request_hints: self.hints.nullifier_read_request_hints, pending_nullifiers: previous_public_inputs.end.new_nullifiers, - master_nullifier_secret_keys: self.master_nullifier_secret_keys, + master_nullifier_secret_keys: self.hints.master_nullifier_secret_keys, nullifier_tree_root }; request_processor.validate(); let mut composer = KernelCircuitPublicInputsComposer::new( self.previous_kernel, - self.sorted_new_note_hashes, - self.sorted_new_note_hashes_indexes, - self.sorted_new_nullifiers, - self.sorted_new_nullifiers_indexes, - self.nullifier_commitment_hints, - self.sorted_encrypted_log_hashes, - self.sorted_encrypted_log_hashes_indexes, - self.sorted_unencrypted_log_hashes, - self.sorted_unencrypted_log_hashes_indexes + self.outputs.note_hashes, + self.outputs.nullifiers, + self.hints.transient_nullifier_indexes_for_note_hashes, + self.hints.transient_note_hash_indexes_for_nullifiers, + self.hints.sorted_new_note_hashes, + self.hints.sorted_new_note_hashes_indexes, + self.hints.sorted_new_nullifiers, + self.hints.sorted_new_nullifiers_indexes, + self.hints.sorted_encrypted_log_hashes, + self.hints.sorted_encrypted_log_hashes_indexes, + self.hints.sorted_unencrypted_log_hashes, + self.hints.sorted_unencrypted_log_hashes_indexes ); composer.compose().finish() } } mod tests { - use crate::private_kernel_tail::PrivateKernelTailCircuitPrivateInputs; + use crate::private_kernel_tail::{PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailHints, PrivateKernelTailOutputs}; use dep::reset_kernel_lib::{ - tests::nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, + tests::{ + nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, + squash_transient_data::{squash_transient_note_hashes, squash_transient_nullifiers} + }, reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} }; use dep::types::constants::{ @@ -76,7 +94,7 @@ mod tests { use dep::types::{ abis::{ kernel_circuit_public_inputs::KernelCircuitPublicInputs, max_block_number::MaxBlockNumber, - side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}, gas::Gas + note_hash::NoteHashContext, nullifier::Nullifier, side_effect::{SideEffect, Ordered}, gas::Gas }, grumpkin_private_key::GrumpkinPrivateKey, hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash, accumulate_sha256}, @@ -84,10 +102,12 @@ mod tests { utils::{arrays::{array_eq, array_length}}, traits::{Empty, is_empty, is_empty_array} }; + // TODO: Reduce the duplicated code/tests for PrivateKernelTailInputs and PrivateKernelTailToPublicInputs. struct PrivateKernelTailInputsBuilder { previous_kernel: FixtureBuilder, - read_commitment_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - nullifier_commitment_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder, } @@ -98,15 +118,19 @@ mod tests { PrivateKernelTailInputsBuilder { previous_kernel, - read_commitment_hints: [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - nullifier_commitment_hints: [0; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], + transient_nullifier_indexes_for_note_hashes: [MAX_NEW_NULLIFIERS_PER_TX; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [MAX_NEW_NOTE_HASHES_PER_TX; MAX_NEW_NULLIFIERS_PER_TX], nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(MAX_NULLIFIER_READ_REQUESTS_PER_TX) } } // A helper function that uses the first nullifer in the previous kernel to compute the unique siloed // note_hashes for the given note_hashes. - pub fn compute_unique_siloed_note_hashes(self, note_hashes: [SideEffect; N]) -> [Field; N] { + pub fn compute_unique_siloed_note_hashes( + self, + note_hashes: [NoteHashContext; N] + ) -> [Field; N] { let first_nullifier = self.previous_kernel.new_nullifiers.get_unchecked(0); let mut unique_siloed_note_hashes = [0; N]; for i in 0..N { @@ -120,7 +144,7 @@ mod tests { pub fn add_pending_note_hash_read_request(&mut self, note_hash_index: u64) { let read_request_index = self.previous_kernel.add_read_request_for_pending_note_hash(note_hash_index); - self.read_commitment_hints[read_request_index] = note_hash_index; + self.note_hash_read_request_hints[read_request_index] = note_hash_index; } pub fn add_pending_nullifier_read_request(&mut self, nullifier_index_offset_one: u64) { @@ -133,35 +157,32 @@ mod tests { } pub fn nullify_pending_note_hash(&mut self, nullifier_index: u64, note_hash_index: u64) { + self.previous_kernel.new_note_hashes.storage[note_hash_index].nullifier_counter = self.previous_kernel.new_nullifiers.get(nullifier_index).counter; self.previous_kernel.new_nullifiers.storage[nullifier_index].note_hash = self.previous_kernel.new_note_hashes.get(note_hash_index).value; - self.nullifier_commitment_hints[nullifier_index] = note_hash_index; + self.transient_nullifier_indexes_for_note_hashes[note_hash_index] = nullifier_index; + self.transient_note_hash_indexes_for_nullifiers[nullifier_index] = note_hash_index; } pub fn execute(&mut self) -> KernelCircuitPublicInputs { let sorted = sort_get_sorted_hints( self.previous_kernel.new_note_hashes.storage, - |a: SideEffect, b: SideEffect| a.counter < b.counter + |a: NoteHashContext, b: NoteHashContext| a.counter < b.counter ); let sorted_new_note_hashes = sorted.sorted_array; let sorted_new_note_hashes_indexes = sorted.sorted_index_hints; - let mut sorted_read_commitment_hints = [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX]; - for i in 0..sorted_read_commitment_hints.len() { - sorted_read_commitment_hints[i] = sorted_new_note_hashes_indexes[self.read_commitment_hints[i]]; + let mut sorted_note_hash_read_request_hints = [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX]; + for i in 0..sorted_note_hash_read_request_hints.len() { + sorted_note_hash_read_request_hints[i] = sorted_new_note_hashes_indexes[self.note_hash_read_request_hints[i]]; } let sorted = sort_get_sorted_hints( self.previous_kernel.new_nullifiers.storage, - |a: SideEffectLinkedToNoteHash, b: SideEffectLinkedToNoteHash| a.counter < b.counter + |a: Nullifier, b: Nullifier| a.counter < b.counter ); let sorted_new_nullifiers = sorted.sorted_array; let sorted_new_nullifiers_indexes = sorted.sorted_index_hints; - let mut sorted_nullifier_commitment_hints = [0; MAX_NEW_NULLIFIERS_PER_TX]; - for i in 0..sorted_nullifier_commitment_hints.len() { - sorted_nullifier_commitment_hints[i] = sorted_new_nullifiers_indexes[self.nullifier_commitment_hints[i]]; - } - let sorted = sort_get_sorted_hints( self.previous_kernel.encrypted_logs_hashes.storage, |a: SideEffect, b: SideEffect| a.counter < b.counter @@ -176,21 +197,46 @@ mod tests { let sorted_unencrypted_log_hashes = sorted.sorted_array; let sorted_unencrypted_log_hashes_indexes = sorted.sorted_index_hints; - let kernel = PrivateKernelTailCircuitPrivateInputs { - previous_kernel: self.previous_kernel.to_private_kernel_data(), + let mut sorted_transient_nullifier_indexes_for_note_hashes = [MAX_NEW_NULLIFIERS_PER_TX; MAX_NEW_NOTE_HASHES_PER_TX]; + for i in 0..self.transient_nullifier_indexes_for_note_hashes.len() { + let old_index = self.transient_nullifier_indexes_for_note_hashes[i]; + if old_index != MAX_NEW_NULLIFIERS_PER_TX { + let new_note_hash_index = sorted_new_note_hashes_indexes[i]; + sorted_transient_nullifier_indexes_for_note_hashes[new_note_hash_index] = sorted_new_nullifiers_indexes[old_index]; + } + } + + let mut sorted_transient_note_hash_indexes_for_nullifiers = [MAX_NEW_NOTE_HASHES_PER_TX; MAX_NEW_NULLIFIERS_PER_TX]; + for i in 0..self.transient_note_hash_indexes_for_nullifiers.len() { + let old_index = self.transient_note_hash_indexes_for_nullifiers[i]; + if old_index != MAX_NEW_NOTE_HASHES_PER_TX { + let new_nullifier_index = sorted_new_nullifiers_indexes[i]; + sorted_transient_note_hash_indexes_for_nullifiers[new_nullifier_index] = sorted_new_note_hashes_indexes[old_index]; + } + } + + let outputs = PrivateKernelTailOutputs { + note_hashes: squash_transient_note_hashes(sorted_new_note_hashes), + nullifiers: squash_transient_nullifiers(sorted_new_nullifiers) + }; + + let hints = PrivateKernelTailHints { + transient_nullifier_indexes_for_note_hashes: sorted_transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers: sorted_transient_note_hash_indexes_for_nullifiers, + note_hash_read_request_hints: sorted_note_hash_read_request_hints, + nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), + master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], sorted_new_note_hashes, sorted_new_note_hashes_indexes, - read_commitment_hints: sorted_read_commitment_hints, sorted_new_nullifiers, sorted_new_nullifiers_indexes, - nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), - nullifier_commitment_hints: sorted_nullifier_commitment_hints, sorted_encrypted_log_hashes, sorted_encrypted_log_hashes_indexes, sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hashes_indexes, - master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX] + sorted_unencrypted_log_hashes_indexes }; + + let kernel = PrivateKernelTailCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), outputs, hints }; kernel.native_private_kernel_circuit_tail() } @@ -212,7 +258,7 @@ mod tests { } #[test] - unconstrained fn native_matching_one_read_request_to_commitment_works() { + unconstrained fn native_matching_one_read_request_to_note_hash_works() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(1); @@ -391,7 +437,40 @@ mod tests { } #[test] - unconstrained fn ordering_of_commitments_and_nullifiers() { + unconstrained fn squash_unordered_transient_notes_works() { + let mut builder = PrivateKernelTailInputsBuilder::new(); + + builder.previous_kernel.append_new_note_hashes(3); + // Shuffle the note hashes so they will have to be re-ordered. + let tmp = builder.previous_kernel.new_note_hashes.storage[0]; + builder.previous_kernel.new_note_hashes.storage[0] = builder.previous_kernel.new_note_hashes.storage[1]; + builder.previous_kernel.new_note_hashes.storage[1] = builder.previous_kernel.new_note_hashes.storage[2]; + builder.previous_kernel.new_note_hashes.storage[2] = tmp; + + builder.previous_kernel.append_new_nullifiers(3); + // Shuffle the nullifers so they will have to be re-ordered. + let tmp = builder.previous_kernel.new_nullifiers.storage[1]; + builder.previous_kernel.new_nullifiers.storage[1] = builder.previous_kernel.new_nullifiers.storage[3]; + builder.previous_kernel.new_nullifiers.storage[3] = builder.previous_kernel.new_nullifiers.storage[2]; + builder.previous_kernel.new_nullifiers.storage[2] = tmp; + + // The nullifier at index 1 is nullifying the note hash at index 1; + builder.nullify_pending_note_hash(1, 1); + // The nullifier at index 2 is nullifying the note hash at index 2; + builder.nullify_pending_note_hash(2, 2); + // The nullifier at index 3 is nullifying the note hash at index 0; + builder.nullify_pending_note_hash(3, 0); + + let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; + let public_inputs = builder.execute(); + + // Only the first nullifier is left after squashing. + assert(is_empty_array(public_inputs.end.new_note_hashes)); + assert(array_eq(public_inputs.end.new_nullifiers, [new_nullifiers[0].value])); + } + + #[test] + unconstrained fn ordering_of_note_hashes_and_nullifiers() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(10); @@ -400,8 +479,8 @@ mod tests { let sorted_note_hashes = builder.previous_kernel.new_note_hashes.storage; let sorted_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let mut reversed_note_hashes = [SideEffect::empty(); 10]; - let mut reversed_nullifiers = [SideEffectLinkedToNoteHash::empty(); 10]; + let mut reversed_note_hashes = [NoteHashContext::empty(); 10]; + let mut reversed_nullifiers = [Nullifier::empty(); 10]; for i in 0..10 { reversed_note_hashes[9 - i] = builder.previous_kernel.new_note_hashes.pop(); @@ -422,7 +501,7 @@ mod tests { } #[test] - unconstrained fn native_empty_nullified_commitment_means_persistent_nullifier_0() { + unconstrained fn native_empty_nullified_note_hash_means_persistent_nullifier_0() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.append_new_nullifiers(2); @@ -430,32 +509,21 @@ mod tests { assert_eq(array_length(public_inputs.end.new_note_hashes), 2); assert_eq(array_length(public_inputs.end.new_nullifiers), 3); } - // same as previous test, but this time there are 0 commitments! - // (Do we really need this test?) - - #[test] - unconstrained fn native_empty_nullified_commitment_means_persistent_nullifier_1() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_new_nullifiers(2); - let public_inputs = builder.execute(); - assert(array_length(public_inputs.end.new_note_hashes) == 0); - assert(array_length(public_inputs.end.new_nullifiers) == 3); - } - #[test(should_fail)] - unconstrained fn invalid_nullifier_commitment_hint_fails() { + #[test(should_fail_with="Hinted note hash does not match")] + unconstrained fn wrong_transient_nullifier_index_for_note_hash_fails() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(1); builder.previous_kernel.append_new_nullifiers(1); // The nullifier at index 1 is nullifying the hash at index 0; builder.nullify_pending_note_hash(1, 0); // Change the hint to be out of bounds. - builder.nullifier_commitment_hints[1] = MAX_NEW_NOTE_HASHES_PER_TX; + builder.transient_nullifier_indexes_for_note_hashes[0] = 0; builder.failed(); } - #[test(should_fail_with="Hinted hash does not match")] - unconstrained fn wrong_nullifier_commitment_hint_fails() { + #[test(should_fail_with="Invalid transient nullifier index hint")] + unconstrained fn wrong_transient_nullifier_index_hint_fails() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.append_new_nullifiers(2); @@ -464,7 +532,7 @@ mod tests { // The nullifier at index 2 is nullifying the hash at index 0; builder.nullify_pending_note_hash(2, 0); // Tweak the hint to be for the hash at index 1. - builder.nullifier_commitment_hints[2] = 1; + builder.transient_note_hash_indexes_for_nullifiers[2] = 1; builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr index 27d9d51a606..a2ba70994b9 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr @@ -3,7 +3,7 @@ use dep::reset_kernel_lib::{NullifierReadRequestHints, PrivateValidationRequestP use dep::types::{ abis::{ kernel_data::PrivateKernelData, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::NoteHashContext, nullifier::Nullifier, side_effect::SideEffect }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, @@ -12,20 +12,32 @@ use dep::types::{ grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length }; -struct PrivateKernelTailToPublicCircuitPrivateInputs { - previous_kernel: PrivateKernelData, - sorted_new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], +// Can just be PublicKernelCircuitPublicInputs. +struct PrivateKernelTailToPublicOutputs { + note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], + nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], +} + +struct PrivateKernelTailToPublicHints { + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], + nullifier_read_request_hints: NullifierReadRequestHints, + master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], + sorted_new_note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], sorted_new_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - read_commitment_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - sorted_new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + sorted_new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], sorted_new_nullifiers_indexes: [u64; MAX_NEW_NULLIFIERS_PER_TX], - nullifier_read_request_hints: NullifierReadRequestHints, - nullifier_commitment_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], sorted_encrypted_log_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX], sorted_encrypted_log_hashes_indexes: [u64; MAX_ENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes: [SideEffect; MAX_UNENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes_indexes: [u64; MAX_UNENCRYPTED_LOGS_PER_TX], - master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], +} + +struct PrivateKernelTailToPublicCircuitPrivateInputs { + previous_kernel: PrivateKernelData, + outputs: PrivateKernelTailToPublicOutputs, + hints: PrivateKernelTailToPublicHints } impl PrivateKernelTailToPublicCircuitPrivateInputs { @@ -38,35 +50,44 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { let nullifier_tree_root = previous_public_inputs.constants.historical_header.state.partial.nullifier_tree.root; let request_processor = PrivateValidationRequestProcessor { validation_requests: previous_public_inputs.validation_requests, - note_hash_read_request_hints: self.read_commitment_hints, + note_hash_read_request_hints: self.hints.note_hash_read_request_hints, pending_note_hashes: previous_public_inputs.end.new_note_hashes, - nullifier_read_request_hints: self.nullifier_read_request_hints, + nullifier_read_request_hints: self.hints.nullifier_read_request_hints, pending_nullifiers: previous_public_inputs.end.new_nullifiers, - master_nullifier_secret_keys: self.master_nullifier_secret_keys, + master_nullifier_secret_keys: self.hints.master_nullifier_secret_keys, nullifier_tree_root }; request_processor.validate(); let mut composer = KernelCircuitPublicInputsComposer::new( self.previous_kernel, - self.sorted_new_note_hashes, - self.sorted_new_note_hashes_indexes, - self.sorted_new_nullifiers, - self.sorted_new_nullifiers_indexes, - self.nullifier_commitment_hints, - self.sorted_encrypted_log_hashes, - self.sorted_encrypted_log_hashes_indexes, - self.sorted_unencrypted_log_hashes, - self.sorted_unencrypted_log_hashes_indexes + self.outputs.note_hashes, + self.outputs.nullifiers, + self.hints.transient_nullifier_indexes_for_note_hashes, + self.hints.transient_note_hash_indexes_for_nullifiers, + self.hints.sorted_new_note_hashes, + self.hints.sorted_new_note_hashes_indexes, + self.hints.sorted_new_nullifiers, + self.hints.sorted_new_nullifiers_indexes, + self.hints.sorted_encrypted_log_hashes, + self.hints.sorted_encrypted_log_hashes_indexes, + self.hints.sorted_unencrypted_log_hashes, + self.hints.sorted_unencrypted_log_hashes_indexes ); composer.compose_public().finish_to_public() } } mod tests { - use crate::private_kernel_tail_to_public::PrivateKernelTailToPublicCircuitPrivateInputs; + use crate::private_kernel_tail_to_public::{ + PrivateKernelTailToPublicCircuitPrivateInputs, PrivateKernelTailToPublicHints, + PrivateKernelTailToPublicOutputs + }; use dep::reset_kernel_lib::{ - tests::nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, + tests::{ + nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, + squash_transient_data::{squash_transient_note_hashes, squash_transient_nullifiers} + }, reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} }; use dep::types::constants::{ @@ -76,7 +97,7 @@ mod tests { use dep::types::{ abis::{ kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, gas::Gas, - side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered} + note_hash::{NoteHash, NoteHashContext}, nullifier::Nullifier, side_effect::{SideEffect, Ordered} }, grumpkin_private_key::GrumpkinPrivateKey, hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash}, @@ -84,10 +105,12 @@ mod tests { utils::{arrays::{array_eq, array_length}}, traits::is_empty_array }; + // TODO: Reduce the duplicated code/tests for PrivateKernelTailToPublicInputs and PrivateKernelTailInputs. struct PrivateKernelTailToPublicInputsBuilder { previous_kernel: FixtureBuilder, - read_commitment_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - nullifier_commitment_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder, } @@ -99,8 +122,9 @@ mod tests { PrivateKernelTailToPublicInputsBuilder { previous_kernel, - read_commitment_hints: [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - nullifier_commitment_hints: [0; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], + transient_nullifier_indexes_for_note_hashes: [MAX_NEW_NULLIFIERS_PER_TX; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [MAX_NEW_NOTE_HASHES_PER_TX; MAX_NEW_NULLIFIERS_PER_TX], nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(MAX_NULLIFIER_READ_REQUESTS_PER_TX) } } @@ -109,16 +133,16 @@ mod tests { // note_hashes for the given note_hashes. pub fn compute_unique_siloed_note_hashes( self, - note_hashes: [SideEffect; N] - ) -> [SideEffect; N] { + note_hashes: [NoteHashContext; N] + ) -> [NoteHash; N] { let first_nullifier = self.previous_kernel.new_nullifiers.get_unchecked(0); - let mut unique_siloed_note_hashes = [SideEffect::empty(); N]; + let mut unique_siloed_note_hashes = [NoteHash::empty(); N]; for i in 0..N { if note_hashes[i].value != 0 { let nonce = compute_note_hash_nonce(first_nullifier.value, i); - unique_siloed_note_hashes[i] = SideEffect { + unique_siloed_note_hashes[i] = NoteHash { value: compute_unique_siloed_note_hash(nonce, note_hashes[i].value), - counter: note_hashes[i].counter, + counter: 0, // Counter is cleared so it's not exposed to the public. }; } } @@ -127,7 +151,7 @@ mod tests { pub fn add_pending_note_hash_read_request(&mut self, note_hash_index: u64) { let read_request_index = self.previous_kernel.add_read_request_for_pending_note_hash(note_hash_index); - self.read_commitment_hints[read_request_index] = note_hash_index; + self.note_hash_read_request_hints[read_request_index] = note_hash_index; } pub fn add_pending_nullifier_read_request(&mut self, nullifier_index_offset_one: u64) { @@ -139,36 +163,33 @@ mod tests { self.nullifier_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; } - pub fn nullify_transient_note_hash(&mut self, nullifier_index: Field, note_hash_index: u64) { + pub fn nullify_pending_note_hash(&mut self, nullifier_index: u64, note_hash_index: u64) { + self.previous_kernel.new_note_hashes.storage[note_hash_index].nullifier_counter = self.previous_kernel.new_nullifiers.get(nullifier_index).counter; self.previous_kernel.new_nullifiers.storage[nullifier_index].note_hash = self.previous_kernel.new_note_hashes.get(note_hash_index).value; - self.nullifier_commitment_hints[nullifier_index] = note_hash_index; + self.transient_nullifier_indexes_for_note_hashes[note_hash_index] = nullifier_index; + self.transient_note_hash_indexes_for_nullifiers[nullifier_index] = note_hash_index; } pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { let sorted = sort_get_sorted_hints( self.previous_kernel.new_note_hashes.storage, - |a: SideEffect, b: SideEffect| a.counter < b.counter + |a: NoteHashContext, b: NoteHashContext| a.counter < b.counter ); let sorted_new_note_hashes = sorted.sorted_array; let sorted_new_note_hashes_indexes = sorted.sorted_index_hints; - let mut sorted_read_commitment_hints = [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX]; - for i in 0..sorted_read_commitment_hints.len() { - sorted_read_commitment_hints[i] = sorted_new_note_hashes_indexes[self.read_commitment_hints[i]]; + let mut sorted_note_hash_read_request_hints = [0; MAX_NOTE_HASH_READ_REQUESTS_PER_TX]; + for i in 0..sorted_note_hash_read_request_hints.len() { + sorted_note_hash_read_request_hints[i] = sorted_new_note_hashes_indexes[self.note_hash_read_request_hints[i]]; } let sorted = sort_get_sorted_hints( self.previous_kernel.new_nullifiers.storage, - |a: SideEffectLinkedToNoteHash, b: SideEffectLinkedToNoteHash| a.counter < b.counter + |a: Nullifier, b: Nullifier| a.counter < b.counter ); let sorted_new_nullifiers = sorted.sorted_array; let sorted_new_nullifiers_indexes = sorted.sorted_index_hints; - let mut sorted_nullifier_commitment_hints = [0; MAX_NEW_NULLIFIERS_PER_TX]; - for i in 0..sorted_nullifier_commitment_hints.len() { - sorted_nullifier_commitment_hints[i] = sorted_new_nullifiers_indexes[self.nullifier_commitment_hints[i]]; - } - let sorted = sort_get_sorted_hints( self.previous_kernel.encrypted_logs_hashes.storage, |a: SideEffect, b: SideEffect| a.counter < b.counter @@ -183,21 +204,46 @@ mod tests { let sorted_unencrypted_log_hashes = sorted.sorted_array; let sorted_unencrypted_log_hashes_indexes = sorted.sorted_index_hints; - let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { - previous_kernel: self.previous_kernel.to_private_kernel_data(), + let mut sorted_transient_nullifier_indexes_for_note_hashes = [MAX_NEW_NULLIFIERS_PER_TX; MAX_NEW_NOTE_HASHES_PER_TX]; + for i in 0..self.transient_nullifier_indexes_for_note_hashes.len() { + let old_index = self.transient_nullifier_indexes_for_note_hashes[i]; + if old_index != MAX_NEW_NULLIFIERS_PER_TX { + let new_note_hash_index = sorted_new_note_hashes_indexes[i]; + sorted_transient_nullifier_indexes_for_note_hashes[new_note_hash_index] = sorted_new_nullifiers_indexes[old_index]; + } + } + + let mut sorted_transient_note_hash_indexes_for_nullifiers = [MAX_NEW_NOTE_HASHES_PER_TX; MAX_NEW_NULLIFIERS_PER_TX]; + for i in 0..self.transient_note_hash_indexes_for_nullifiers.len() { + let old_index = self.transient_note_hash_indexes_for_nullifiers[i]; + if old_index != MAX_NEW_NOTE_HASHES_PER_TX { + let new_nullifier_index = sorted_new_nullifiers_indexes[i]; + sorted_transient_note_hash_indexes_for_nullifiers[new_nullifier_index] = sorted_new_note_hashes_indexes[old_index]; + } + } + + let outputs = PrivateKernelTailToPublicOutputs { + note_hashes: squash_transient_note_hashes(sorted_new_note_hashes), + nullifiers: squash_transient_nullifiers(sorted_new_nullifiers) + }; + + let hints = PrivateKernelTailToPublicHints { + transient_nullifier_indexes_for_note_hashes: sorted_transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers: sorted_transient_note_hash_indexes_for_nullifiers, + note_hash_read_request_hints: sorted_note_hash_read_request_hints, + nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), + master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], sorted_new_note_hashes, sorted_new_note_hashes_indexes, - read_commitment_hints: sorted_read_commitment_hints, sorted_new_nullifiers, sorted_new_nullifiers_indexes, - nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), - nullifier_commitment_hints: sorted_nullifier_commitment_hints, sorted_encrypted_log_hashes, sorted_encrypted_log_hashes_indexes, sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hashes_indexes, - master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX] + sorted_unencrypted_log_hashes_indexes }; + + let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), outputs, hints }; kernel.execute() } @@ -211,7 +257,7 @@ mod tests { } #[test] - unconstrained fn native_matching_one_read_request_to_commitment_works() { + unconstrained fn native_matching_one_read_request_to_note_hash_works() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(1); @@ -229,7 +275,7 @@ mod tests { } #[test] - unconstrained fn native_matching_some_read_requests_to_commitments_works() { + unconstrained fn native_matching_some_read_requests_to_note_hashes_works() { let num_non_revertible = 3; let num_revertible = 2; @@ -324,7 +370,7 @@ mod tests { builder.previous_kernel.append_new_note_hashes(1); builder.previous_kernel.append_new_nullifiers(2); // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_transient_note_hash(1, 0); + builder.nullify_pending_note_hash(1, 0); let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; let public_inputs = builder.execute(); assert(is_empty_array(public_inputs.end.new_note_hashes)); @@ -344,7 +390,7 @@ mod tests { builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.append_new_nullifiers(2); // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_transient_note_hash(1, 0); + builder.nullify_pending_note_hash(1, 0); let new_note_hashes = builder.previous_kernel.new_note_hashes.storage; // The 0th hash will be chopped. let unique_siloed_note_hashes = builder.compute_unique_siloed_note_hashes([new_note_hashes[1]]); @@ -371,9 +417,9 @@ mod tests { builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.append_new_nullifiers(2); // The nullifier at index 1 is nullifying the hash at index 1; - builder.nullify_transient_note_hash(1, 1); + builder.nullify_pending_note_hash(1, 1); // The nullifier at index 2 is nullifying the hash at index 0; - builder.nullify_transient_note_hash(2, 0); + builder.nullify_pending_note_hash(2, 0); let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; let public_inputs = builder.execute(); @@ -392,8 +438,8 @@ mod tests { let sorted_note_hashes = builder.previous_kernel.new_note_hashes.storage; let sorted_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let mut reversed_note_hashes = [SideEffect::empty(); 10]; - let mut reversed_nullifiers = [SideEffectLinkedToNoteHash::empty(); 10]; + let mut reversed_note_hashes = [NoteHashContext::empty(); 10]; + let mut reversed_nullifiers = [Nullifier::empty(); 10]; for i in 0..10 { reversed_note_hashes[9 - i] = builder.previous_kernel.new_note_hashes.pop(); @@ -413,29 +459,29 @@ mod tests { } } - #[test(should_fail)] - unconstrained fn invalid_nullifier_commitment_hint_fails() { + #[test(should_fail_with="Hinted note hash does not match")] + unconstrained fn wrong_transient_nullifier_index_for_note_hash_fails() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(1); builder.previous_kernel.append_new_nullifiers(1); // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_transient_note_hash(1, 0); + builder.nullify_pending_note_hash(1, 0); // Change the hint to be out of bounds. - builder.nullifier_commitment_hints[1] = MAX_NEW_NOTE_HASHES_PER_TX; + builder.transient_nullifier_indexes_for_note_hashes[0] = 0; builder.failed(); } - #[test(should_fail_with="Hinted hash does not match")] - unconstrained fn wrong_nullifier_commitment_hint_fails() { + #[test(should_fail_with="Invalid transient nullifier index hint")] + unconstrained fn wrong_transient_nullifier_index_hint_fails() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.append_new_nullifiers(2); // The nullifier at index 1 is nullifying the hash at index 1; - builder.nullify_transient_note_hash(1, 1); + builder.nullify_pending_note_hash(1, 1); // The nullifier at index 2 is nullifying the hash at index 0; - builder.nullify_transient_note_hash(2, 0); + builder.nullify_pending_note_hash(2, 0); // Tweak the hint to be for the hash at index 1. - builder.nullifier_commitment_hints[2] = 1; + builder.transient_note_hash_indexes_for_nullifiers[2] = 1; builder.failed(); } @@ -489,14 +535,14 @@ mod tests { } #[test] - unconstrained fn split_commitments_into_non_revertible() { + unconstrained fn split_note_hashes_into_non_revertible() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - // expect 2 non-revertible commitments + // expect 2 non-revertible note hashes builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.end_setup(); - // expect 2 revertible commitments + // expect 2 revertible note hashes builder.previous_kernel.append_new_note_hashes(2); let new_note_hashes = builder.previous_kernel.new_note_hashes.storage; @@ -529,7 +575,7 @@ mod tests { // nullify it in revertible part builder.previous_kernel.append_new_nullifiers(1); - builder.nullify_transient_note_hash(1, 0); + builder.nullify_pending_note_hash(1, 0); let public_inputs = builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr index 02b12081a97..d25e42f490a 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr @@ -2,9 +2,9 @@ use dep::types::{ abis::{ call_request::CallRequest, public_call_stack_item::PublicCallStackItem, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputsBuilder, kernel_data::PublicKernelData, - public_call_data::PublicCallData, public_data_read::PublicDataRead, - public_data_update_request::PublicDataUpdateRequest, read_request::ReadRequestContext, - side_effect::{SideEffect, SideEffectLinkedToNoteHash}, global_variables::GlobalVariables, + note_hash::NoteHash, nullifier::Nullifier, public_call_data::PublicCallData, + public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, + read_request::ReadRequestContext, side_effect::SideEffect, global_variables::GlobalVariables, combined_constant_data::CombinedConstantData }, address::AztecAddress, @@ -416,12 +416,12 @@ fn propagate_new_note_hashes_non_revertible( let new_note_hashes = public_call.call_stack_item.public_inputs.new_note_hashes; let storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; - let mut siloed_new_note_hashes : BoundedVec = BoundedVec::new(); + let mut siloed_new_note_hashes : BoundedVec = BoundedVec::new(); for i in 0..MAX_NEW_NOTE_HASHES_PER_CALL { let new_note_hash = new_note_hashes[i].value; if new_note_hash != 0 { let siloed_new_note_hash = silo_note_hash(storage_contract_address, new_note_hash); - siloed_new_note_hashes.push(SideEffect { value: siloed_new_note_hash, counter: new_note_hashes[i].counter }); + siloed_new_note_hashes.push(NoteHash { value: siloed_new_note_hash, counter: new_note_hashes[i].counter }); } } circuit_outputs.end_non_revertible.new_note_hashes.extend_from_bounded_vec(siloed_new_note_hashes); @@ -436,12 +436,12 @@ fn propagate_new_note_hashes( let new_note_hashes = public_call.call_stack_item.public_inputs.new_note_hashes; let storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; - let mut siloed_new_note_hashes : BoundedVec = BoundedVec::new(); + let mut siloed_new_note_hashes : BoundedVec = BoundedVec::new(); for i in 0..MAX_NEW_NOTE_HASHES_PER_CALL { let new_note_hash = new_note_hashes[i].value; if new_note_hash != 0 { let siloed_new_note_hash = silo_note_hash(storage_contract_address, new_note_hash); - siloed_new_note_hashes.push(SideEffect { value: siloed_new_note_hash, counter: new_note_hashes[i].counter }); + siloed_new_note_hashes.push(NoteHash { value: siloed_new_note_hash, counter: new_note_hashes[i].counter }); } } circuit_outputs.end.new_note_hashes.extend_from_bounded_vec(siloed_new_note_hashes); @@ -455,13 +455,13 @@ fn propagate_new_nullifiers_non_revertible( let storage_contract_address = public_call.call_stack_item.public_inputs.call_context.storage_contract_address; // Enhance commitments and nullifiers with domain separation whereby domain is the contract. - let mut siloed_new_nullifiers : BoundedVec = BoundedVec::new(); + let mut siloed_new_nullifiers : BoundedVec = BoundedVec::new(); for i in 0..MAX_NEW_NULLIFIERS_PER_CALL { let new_nullifier = new_nullifiers[i].value; if new_nullifier != 0 { let siloed_new_nullifier = silo_nullifier(storage_contract_address, new_nullifier); siloed_new_nullifiers.push( - SideEffectLinkedToNoteHash { + Nullifier { value: siloed_new_nullifier, counter: new_nullifiers[i].counter, note_hash: new_nullifiers[i].note_hash @@ -481,13 +481,13 @@ fn propagate_new_nullifiers( let storage_contract_address = public_call.call_stack_item.public_inputs.call_context.storage_contract_address; // Enhance commitments and nullifiers with domain separation whereby domain is the contract. - let mut siloed_new_nullifiers : BoundedVec = BoundedVec::new(); + let mut siloed_new_nullifiers : BoundedVec = BoundedVec::new(); for i in 0..MAX_NEW_NULLIFIERS_PER_CALL { let new_nullifier = new_nullifiers[i].value; if new_nullifier != 0 { let siloed_new_nullifier = silo_nullifier(storage_contract_address, new_nullifier); siloed_new_nullifiers.push( - SideEffectLinkedToNoteHash { + Nullifier { value: siloed_new_nullifier, counter: new_nullifiers[i].counter, note_hash: new_nullifiers[i].note_hash diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr index bb4985312fd..d6123c39ac6 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr @@ -78,8 +78,8 @@ mod tests { use dep::types::{ abis::{ gas::Gas, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, - public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, - read_request::ReadRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::{NoteHash, NoteHashContext}, nullifier::Nullifier, public_data_read::PublicDataRead, + public_data_update_request::PublicDataUpdateRequest, read_request::ReadRequest }, address::{AztecAddress, EthAddress}, contract_class_id::ContractClassId, hash::{compute_l2_to_l1_hash, silo_note_hash, silo_nullifier}, @@ -186,16 +186,16 @@ mod tests { let contract_address = builder.public_call.contract_address; // Setup 2 new note hashes on the previous kernel. builder.previous_kernel.append_new_note_hashes(2); - let previous = builder.previous_kernel.new_note_hashes.storage; + let previous = builder.previous_kernel.new_note_hashes.storage.map(|n: NoteHashContext| n.to_note_hash()); // Setup 2 new note hashes on the current public inputs. let current = [ - SideEffect { value: previous[1].value + 1, counter: 3 }, - SideEffect { value: previous[1].value + 2, counter: 4 } + NoteHash { value: previous[1].value + 1, counter: 3 }, + NoteHash { value: previous[1].value + 2, counter: 4 } ]; builder.public_call.public_inputs.new_note_hashes.extend_from_array(current); - let siloed = current.map(|c: SideEffect| silo_note_hash(contract_address, c.value)); + let siloed = current.map(|c: NoteHash| silo_note_hash(contract_address, c.value)); let new_note_hashes = [ - previous[0], previous[1], SideEffect { value: siloed[0], counter: 3 }, SideEffect { value: siloed[1], counter: 4 } + previous[0], previous[1], NoteHash { value: siloed[0], counter: 3 }, NoteHash { value: siloed[1], counter: 4 } ]; let public_inputs = builder.execute(); @@ -251,12 +251,12 @@ mod tests { // Setup 2 new note hashes on the current public inputs. let current = [ - SideEffectLinkedToNoteHash { value: previous[1].value + 1, note_hash: 0, counter: 4 }, SideEffectLinkedToNoteHash { value: previous[1].value + 2, note_hash: 0, counter: 5 } + Nullifier { value: previous[1].value + 1, note_hash: 0, counter: 4 }, Nullifier { value: previous[1].value + 2, note_hash: 0, counter: 5 } ]; let siloed = current.map( - |current: SideEffectLinkedToNoteHash| - SideEffectLinkedToNoteHash { value: silo_nullifier(contract_address, current.value), note_hash: current.note_hash, counter: current.counter } + |current: Nullifier| + Nullifier { value: silo_nullifier(contract_address, current.value), note_hash: current.note_hash, counter: current.counter } ); builder.public_call.public_inputs.new_nullifiers.extend_from_array(current); @@ -344,7 +344,7 @@ mod tests { fn public_kernel_fails_creating_new_note_hashes_on_static_call() { let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); builder.public_call.public_inputs.call_context.is_static_call = true; - builder.public_call.public_inputs.new_note_hashes.push(SideEffect { value: 1, counter: 0 }); + builder.public_call.public_inputs.new_note_hashes.push(NoteHash { value: 1, counter: 0 }); builder.failed(); } @@ -353,7 +353,7 @@ mod tests { fn public_kernel_fails_creating_new_nullifiers_on_static_call() { let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); builder.public_call.public_inputs.call_context.is_static_call = true; - builder.public_call.public_inputs.new_nullifiers.push(SideEffectLinkedToNoteHash { value: 1, note_hash: 0, counter: 0 }); + builder.public_call.public_inputs.new_nullifiers.push(Nullifier { value: 1, note_hash: 0, counter: 0 }); builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr index 8a7ffb5e38b..92aee42e17f 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr @@ -3,6 +3,7 @@ use nullifier_read_request_reset::NullifierReadRequestHints; use private_validation_request_processor::PrivateValidationRequestProcessor; use public_data_read_request_reset::PublicDataReadRequestHints; use public_validation_request_processor::PublicValidationRequestProcessor; +use reset::transient_data::verify_squashed_transient_note_hashes_and_nullifiers; use types::public_data_hint::PublicDataHint; mod nullifier_non_existent_read_request_reset; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_non_existent_read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_non_existent_read_request_reset.nr index 5f9f7548782..10246f1ba6e 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_non_existent_read_request_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_non_existent_read_request_reset.nr @@ -1,6 +1,6 @@ use crate::reset::non_existent_read_request::NonMembershipHint; use dep::types::{ - abis::{nullifier_leaf_preimage::NullifierLeafPreimage, side_effect::SideEffectLinkedToNoteHash}, + abis::{nullifier::Nullifier, nullifier_leaf_preimage::NullifierLeafPreimage}, merkle_tree::MembershipWitness, constants::{MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, NULLIFIER_TREE_HEIGHT} }; @@ -22,7 +22,7 @@ impl NonMembershipHint for Nullifi struct NullifierNonExistentReadRequestHints { non_membership_hints: [NullifierNonMembershipHint; MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX], - sorted_pending_values: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + sorted_pending_values: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], sorted_pending_value_index_hints: [u64; MAX_NEW_NULLIFIERS_PER_TX], next_pending_value_indices: [u64; MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX], } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr index 8cfa430a8a0..78b018a681c 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr @@ -1,6 +1,6 @@ use crate::{nullifier_read_request_reset::NullifierReadRequestHints, reset::read_request::reset_read_requests}; use dep::types::{ - abis::{side_effect::{SideEffect, SideEffectLinkedToNoteHash}, validation_requests::ValidationRequests}, + abis::{note_hash::NoteHashContext, nullifier::Nullifier, validation_requests::ValidationRequests}, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GENERATOR_INDEX__NSK_M @@ -11,9 +11,9 @@ use dep::types::{ struct PrivateValidationRequestProcessor { validation_requests: ValidationRequests, note_hash_read_request_hints: [u64; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], - pending_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], + pending_note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], nullifier_read_request_hints: NullifierReadRequestHints, - pending_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + pending_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], nullifier_tree_root: Field } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr index 4a74f99fd5b..a3fd6a84cce 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/public_validation_request_processor.nr @@ -9,9 +9,8 @@ use crate::{ }; use dep::types::{ abis::{ - kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, - public_data_update_request::PublicDataUpdateRequest, side_effect::SideEffectLinkedToNoteHash, - validation_requests::ValidationRequests + kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, nullifier::Nullifier, + public_data_update_request::PublicDataUpdateRequest, validation_requests::ValidationRequests }, constants::{MAX_NEW_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX}, hash::silo_nullifier, traits::is_empty, @@ -20,7 +19,7 @@ use dep::types::{ struct PublicValidationRequestProcessor { validation_requests: ValidationRequests, - pending_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + pending_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], pending_public_data_writes: [PublicDataUpdateRequest; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], nullifier_read_request_hints: NullifierReadRequestHints, nullifier_non_existent_read_request_hints: NullifierNonExistentReadRequestHints, @@ -99,7 +98,7 @@ impl PublicValidationRequestProcessor { self.pending_nullifiers, hints.sorted_pending_values, hints.sorted_pending_value_index_hints, - |a: SideEffectLinkedToNoteHash, b: SideEffectLinkedToNoteHash| a.value.lt(b.value) + |a: Nullifier, b: Nullifier| a.value.lt(b.value) ); let sorted_pending_nullifiers = array_to_bounded_vec(hints.sorted_pending_values); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr index 8b98420b3cd..92b72993869 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr @@ -1,3 +1,4 @@ mod mutable_data_read_request; mod non_existent_read_request; mod read_request; +mod transient_data; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr new file mode 100644 index 00000000000..56ef524f2dd --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr @@ -0,0 +1,280 @@ +use dep::types::{abis::{note_hash::NoteHashContext, nullifier::Nullifier}, traits::is_empty}; + +pub fn verify_squashed_transient_note_hashes_and_nullifiers( + note_hashes: [NoteHashContext; NUM_NOTE_HASHES], + nullifiers: [Nullifier; NUM_NULLIFIERS], + expected_note_hashes: [NoteHashContext; NUM_NOTE_HASHES], + expected_nullifiers: [Nullifier; NUM_NULLIFIERS], + transient_nullifier_indexes_for_note_hashes: [u64; NUM_NOTE_HASHES], + transient_note_hash_indexes_for_nullifiers: [u64; NUM_NULLIFIERS] +) { + let mut note_hashes_kept = 0; + let mut note_hashes_removed = 0; + for i in 0..NUM_NOTE_HASHES { + let note_hash = note_hashes[i]; + let nullifier_index = transient_nullifier_indexes_for_note_hashes[i]; + if nullifier_index == NUM_NULLIFIERS { // The note hash has no corresponding nullifier. + assert_eq(expected_note_hashes[note_hashes_kept], note_hash, "Propagated note hash does not match"); + note_hashes_kept += 1; + } else { + let nullifier = nullifiers[nullifier_index]; + assert_eq(note_hash.value, nullifier.note_hash, "Hinted note hash does not match"); + assert_eq( + note_hash.nullifier_counter, nullifier.counter, "Hinted nullifier counter does not match" + ); + + // The following check is not necessary as the nullifier_counter is assured to be greater than the counter of + // the note hash when propagated from either the initial or inner private kernel circuits. + // assert(nullifier.counter > note_hash.counter); + + note_hashes_removed += 1; + + // For each note hash removed, an empty item is padded to the right. + let padded_note_hash = expected_note_hashes[NUM_NOTE_HASHES - note_hashes_removed]; + assert(is_empty(padded_note_hash), "Empty note hash must be padded to the right"); + } + } + + let mut nullifiers_kept = 0; + let mut nullifiers_removed = 0; + for i in 0..NUM_NULLIFIERS { + let nullifier = nullifiers[i]; + let index_hint = transient_note_hash_indexes_for_nullifiers[i]; + if index_hint == NUM_NOTE_HASHES { + assert_eq(expected_nullifiers[nullifiers_kept], nullifier, "Propagated nullifier does not match"); + nullifiers_kept += 1; + } else { + let transient_nullifier_index = transient_nullifier_indexes_for_note_hashes[index_hint]; + assert_eq(transient_nullifier_index, i, "Invalid transient nullifier index hint"); + + nullifiers_removed += 1; + + // For each note hash removed, an empty item is padded to the right. + let padded_nullifier = expected_nullifiers[NUM_NULLIFIERS - nullifiers_removed]; + assert(is_empty(padded_nullifier), "Empty nullifier must be padded to the right"); + } + } + + assert_eq( + note_hashes_removed, nullifiers_removed, "Inconsistent number of note hashes and nullifiers removed" + ); +} + +mod tests { + use crate::reset::transient_data::verify_squashed_transient_note_hashes_and_nullifiers; + use dep::types::{abis::{note_hash::NoteHashContext, nullifier::Nullifier}}; + + struct TestDataBuilder { + num_note_hashes: u64, + num_nullifiers: u64, + note_hashes: [NoteHashContext; NUM_NOTE_HASHES], + nullifiers: [Nullifier; NUM_NULLIFIERS], + expected_note_hashes: [NoteHashContext; NUM_NOTE_HASHES], + expected_nullifiers: [Nullifier; NUM_NULLIFIERS], + transient_nullifier_indexes_for_note_hashes: [u64; NUM_NOTE_HASHES], + transient_note_hash_indexes_for_nullifiers: [u64; NUM_NULLIFIERS], + } + + impl TestDataBuilder { + pub fn default() -> TestDataBuilder<3, 3> { + let note_hashes = [ + NoteHashContext { value: 11, counter: 100, nullifier_counter: 500 }, + NoteHashContext { value: 22, counter: 200, nullifier_counter: 0 }, + NoteHashContext { value: 33, counter: 300, nullifier_counter: 400 } + ]; + + let nullifiers = [ + Nullifier { value: 44, counter: 400, note_hash: 33 }, + Nullifier { value: 55, counter: 500, note_hash: 11 }, + Nullifier { value: 66, counter: 600, note_hash: 0 } + ]; + + let expected_note_hashes = [note_hashes[1], NoteHashContext::empty(), NoteHashContext::empty()]; + let expected_nullifiers = [nullifiers[2], Nullifier::empty(), Nullifier::empty()]; + + let transient_nullifier_indexes_for_note_hashes = [1, 3, 0]; + let transient_note_hash_indexes_for_nullifiers = [2, 0, 3]; + + TestDataBuilder { + num_note_hashes: 3, + num_nullifiers: 3, + note_hashes, + nullifiers, + expected_note_hashes, + expected_nullifiers, + transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers + } + } + + pub fn default_all_clear() -> TestDataBuilder<3, 3> { + let note_hashes = [ + NoteHashContext { value: 11, counter: 100, nullifier_counter: 500 }, + NoteHashContext { value: 22, counter: 200, nullifier_counter: 600 }, + NoteHashContext { value: 33, counter: 300, nullifier_counter: 400 } + ]; + + let nullifiers = [ + Nullifier { value: 44, counter: 400, note_hash: 33 }, + Nullifier { value: 55, counter: 500, note_hash: 11 }, + Nullifier { value: 66, counter: 600, note_hash: 22 } + ]; + + let expected_note_hashes = [NoteHashContext::empty(); 3]; + let expected_nullifiers = [Nullifier::empty(); 3]; + + let transient_nullifier_indexes_for_note_hashes = [1, 2, 0]; + let transient_note_hash_indexes_for_nullifiers = [2, 0, 1]; + + TestDataBuilder { + num_note_hashes: 3, + num_nullifiers: 3, + note_hashes, + nullifiers, + expected_note_hashes, + expected_nullifiers, + transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers + } + } + + pub fn verify(self) { + verify_squashed_transient_note_hashes_and_nullifiers( + self.note_hashes, + self.nullifiers, + self.expected_note_hashes, + self.expected_nullifiers, + self.transient_nullifier_indexes_for_note_hashes, + self.transient_note_hash_indexes_for_nullifiers + ); + } + } + + #[test] + fn verify_squahed_transient_note_hashes_and_nullifiers_with_propagated_values() { + TestDataBuilder::default().verify(); + } + + #[test] + fn verify_full_transient_note_hashes_and_nullifiers() { + TestDataBuilder::default_all_clear().verify(); + } + + #[test] + fn verify_partial_transient_note_hashes_and_nullifiers() { + let mut builder = TestDataBuilder::default_all_clear(); + + // Keep the note at index 1. + builder.transient_nullifier_indexes_for_note_hashes[1] = builder.num_nullifiers; + builder.expected_note_hashes[0] = builder.note_hashes[1]; + + // Keep the nullifier at index 2. + builder.transient_note_hash_indexes_for_nullifiers[2] = builder.num_note_hashes; + builder.expected_nullifiers[0] = builder.nullifiers[2]; + + builder.verify(); + } + + #[test(should_fail_with="Hinted note hash does not match")] + fn mismatch_note_hash_value() { + let mut builder = TestDataBuilder::default_all_clear(); + + builder.note_hashes[1].value += 1; + + builder.verify(); + } + + #[test(should_fail_with="Hinted nullifier counter does not match")] + fn mismatch_nullifier_counter() { + let mut builder = TestDataBuilder::default_all_clear(); + + builder.note_hashes[1].nullifier_counter += 1; + + builder.verify(); + } + + #[test(should_fail_with="Empty note hash must be padded to the right")] + fn unexpected_note_hash_value() { + let mut builder = TestDataBuilder::default_all_clear(); + + builder.expected_note_hashes[2].value = 11; + + builder.verify(); + } + + #[test(should_fail_with="Propagated note hash does not match")] + fn wrong_expected_note_hash_value() { + let mut builder = TestDataBuilder::default(); + + builder.expected_note_hashes[0].value += 1; + + builder.verify(); + } + + #[test(should_fail_with="Propagated note hash does not match")] + fn wrong_expected_note_hash_counter() { + let mut builder = TestDataBuilder::default(); + + builder.expected_note_hashes[0].counter += 1; + + builder.verify(); + } + + #[test(should_fail_with="Invalid transient nullifier index hint")] + fn wrong_hint_for_transient_nullifier_index() { + let mut builder = TestDataBuilder::default_all_clear(); + + builder.transient_note_hash_indexes_for_nullifiers[0] = 1; + + builder.verify(); + } + + #[test(should_fail_with="Empty nullifier must be padded to the right")] + fn unexpected_nullifier_value() { + let mut builder = TestDataBuilder::default_all_clear(); + + builder.expected_nullifiers[2].value = 11; + + builder.verify(); + } + + #[test(should_fail_with="Propagated nullifier does not match")] + fn wrong_expected_nullifier_value() { + let mut builder = TestDataBuilder::default(); + + builder.expected_nullifiers[0].value += 1; + + builder.verify(); + } + + #[test(should_fail_with="Propagated nullifier does not match")] + fn wrong_expected_nullifier_counter() { + let mut builder = TestDataBuilder::default(); + + builder.expected_nullifiers[0].counter += 1; + + builder.verify(); + } + + #[test(should_fail_with="Invalid transient nullifier index hint")] + fn propagate_more_note_hashes_than_nullifiers() { + let mut builder = TestDataBuilder::default_all_clear(); + + // Keep the note at index 1. + builder.transient_nullifier_indexes_for_note_hashes[1] = builder.num_nullifiers; + builder.expected_note_hashes[0] = builder.note_hashes[1]; + + builder.verify(); + } + + #[test(should_fail_with="Inconsistent number of note hashes and nullifiers removed")] + fn propagate_more_nullifiers_than_note_hashes() { + let mut builder = TestDataBuilder::default_all_clear(); + + // Keep the nullifier at index 2. + builder.transient_note_hash_indexes_for_nullifiers[2] = builder.num_note_hashes; + builder.expected_nullifiers[0] = builder.nullifiers[2]; + + builder.verify(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests.nr index 124f52c1eb6..215c75e2800 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests.nr @@ -1,3 +1,4 @@ mod nullifier_non_existent_read_request_hints_builder; mod nullifier_read_request_hints_builder; mod public_data_read_request_hints_builder; +mod squash_transient_data; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr index ece95ef901c..7d05daec74a 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr @@ -1,6 +1,6 @@ use crate::nullifier_non_existent_read_request_reset::{NullifierNonMembershipHint, NullifierNonExistentReadRequestHints}; use dep::types::{ - abis::{nullifier_leaf_preimage::NullifierLeafPreimage, side_effect::SideEffectLinkedToNoteHash}, + abis::{nullifier::Nullifier, nullifier_leaf_preimage::NullifierLeafPreimage}, constants::{ MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, NULLIFIER_TREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_HEIGHT @@ -14,7 +14,7 @@ struct NullifierNonExistentReadRequestHintsBuilder { nullifier_tree: NonEmptyMerkleTree, non_membership_hints: BoundedVec, read_values: BoundedVec, - pending_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + pending_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], } impl NullifierNonExistentReadRequestHintsBuilder { @@ -23,7 +23,7 @@ impl NullifierNonExistentReadRequestHintsBuilder { nullifier_tree: NonEmptyMerkleTree::empty(), non_membership_hints: BoundedVec::new(), read_values: BoundedVec::new(), - pending_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_TX] + pending_nullifiers: [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_TX] } } @@ -34,10 +34,7 @@ impl NullifierNonExistentReadRequestHintsBuilder { self.nullifier_tree = tree; } - pub fn set_nullifiers( - &mut self, - nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX] - ) { + pub fn set_nullifiers(&mut self, nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX]) { self.pending_nullifiers = nullifiers; } @@ -56,7 +53,7 @@ impl NullifierNonExistentReadRequestHintsBuilder { pub fn to_hints(self) -> NullifierNonExistentReadRequestHints { let sorted_result = sort_get_sorted_hints( self.pending_nullifiers, - |a: SideEffectLinkedToNoteHash, b: SideEffectLinkedToNoteHash| a.value.lt(b.value) + |a: Nullifier, b: Nullifier| a.value.lt(b.value) ); let sorted_pending_values = sorted_result.sorted_array; let sorted_pending_value_index_hints = sorted_result.sorted_index_hints; @@ -65,7 +62,7 @@ impl NullifierNonExistentReadRequestHintsBuilder { for i in 0..MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX { if i < self.read_values.len() { let value = self.read_values.get_unchecked(i); - next_pending_value_indices[i] = find_index(sorted_pending_values, |v: SideEffectLinkedToNoteHash| !v.value.lt(value)); + next_pending_value_indices[i] = find_index(sorted_pending_values, |v: Nullifier| !v.value.lt(value)); } } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/squash_transient_data.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/squash_transient_data.nr new file mode 100644 index 00000000000..48446480718 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/squash_transient_data.nr @@ -0,0 +1,31 @@ +use dep::types::abis::{note_hash::NoteHashContext, nullifier::Nullifier}; + +pub fn squash_transient_note_hashes(note_hashes: [NoteHashContext; N]) -> [NoteHashContext; N] { + let mut final_note_hashes = [NoteHashContext::empty(); N]; + + let mut num_note_hashes = 0; + for i in 0..N { + let note_hash = note_hashes[i]; + if note_hash.nullifier_counter == 0 { + final_note_hashes[num_note_hashes] = note_hash; + num_note_hashes += 1; + } + } + + final_note_hashes +} + +pub fn squash_transient_nullifiers(nullifiers: [Nullifier; N]) -> [Nullifier; N] { + let mut final_nullifiers = [Nullifier::empty(); N]; + + let mut num_nullifiers = 0; + for i in 0..N { + let nullifier = nullifiers[i]; + if nullifier.note_hash == 0 { + final_nullifiers[num_nullifiers] = nullifier; + num_nullifiers += 1; + } + } + + final_nullifiers +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index b6e59abff55..38434b609a3 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -12,8 +12,7 @@ use dep::types::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, membership_witness::{ArchiveRootMembershipWitness, NullifierMembershipWitness, PublicDataMembershipWitness}, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_update_request::PublicDataUpdateRequest, - public_data_read::PublicDataRead, kernel_data::KernelData, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + public_data_read::PublicDataRead, kernel_data::KernelData }, constants::{ NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, @@ -727,11 +726,9 @@ mod tests { let mut builder = BaseRollupInputsBuilder::new(); let new_note_hashes = [27, 28, 29, 30, 31, 32]; - let mut new_note_hashes_vec = builder.kernel_data.new_note_hashes; for i in 0..new_note_hashes.len() { - new_note_hashes_vec.push(SideEffect { value: new_note_hashes[i], counter: 0 }); + builder.kernel_data.add_new_note_hash(new_note_hashes[i]); } - builder.kernel_data.new_note_hashes = new_note_hashes_vec; let mut expected_commitments_tree = NonEmptyMerkleTree::new( [0; MAX_NEW_NOTE_HASHES_PER_TX * 2], [0; NOTE_HASH_TREE_HEIGHT], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr index 23762fd5bf3..13c94c9bf8f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr @@ -15,6 +15,8 @@ mod combined_constant_data; mod side_effect; mod read_request; +mod note_hash; +mod nullifier; mod nullifier_key_validation_request; mod public_data_read; mod public_data_update_request; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr index 2e23a804ce1..ef21c2a34a6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr @@ -1,9 +1,9 @@ use crate::{ hash::compute_tx_logs_hash, abis::{ - accumulated_data::public_accumulated_data::PublicAccumulatedData, - public_data_update_request::PublicDataUpdateRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash}, gas::Gas + accumulated_data::public_accumulated_data::PublicAccumulatedData, note_hash::NoteHash, + nullifier::Nullifier, public_data_update_request::PublicDataUpdateRequest, side_effect::SideEffect, + gas::Gas }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, @@ -33,16 +33,28 @@ struct CombinedAccumulatedData { impl CombinedAccumulatedData { pub fn combine(non_revertible: PublicAccumulatedData, revertible: PublicAccumulatedData) -> Self { // TODO(Miranda): Hash here or elsewhere? - let encrypted_logs_hash = compute_tx_logs_hash(array_merge(non_revertible.encrypted_logs_hashes, revertible.encrypted_logs_hashes)); - let unencrypted_logs_hash = compute_tx_logs_hash(array_merge(non_revertible.unencrypted_logs_hashes, revertible.unencrypted_logs_hashes)); + let encrypted_logs_hash = compute_tx_logs_hash( + array_merge( + non_revertible.encrypted_logs_hashes, + revertible.encrypted_logs_hashes + ) + ); + let unencrypted_logs_hash = compute_tx_logs_hash( + array_merge( + non_revertible.unencrypted_logs_hashes, + revertible.unencrypted_logs_hashes + ) + ); CombinedAccumulatedData { - new_note_hashes: array_merge(non_revertible.new_note_hashes, revertible.new_note_hashes).map(|n: SideEffect| n.value), - new_nullifiers: array_merge(non_revertible.new_nullifiers, revertible.new_nullifiers).map(|n: SideEffectLinkedToNoteHash| n.value), + new_note_hashes: array_merge(non_revertible.new_note_hashes, revertible.new_note_hashes).map(|n: NoteHash| n.value), + new_nullifiers: array_merge(non_revertible.new_nullifiers, revertible.new_nullifiers).map(|n: Nullifier| n.value), new_l2_to_l1_msgs: revertible.new_l2_to_l1_msgs, - encrypted_logs_hash: encrypted_logs_hash, - unencrypted_logs_hash: unencrypted_logs_hash, - encrypted_log_preimages_length: non_revertible.encrypted_log_preimages_length + revertible.encrypted_log_preimages_length, - unencrypted_log_preimages_length: non_revertible.unencrypted_log_preimages_length + revertible.unencrypted_log_preimages_length, + encrypted_logs_hash, + unencrypted_logs_hash, + encrypted_log_preimages_length: non_revertible.encrypted_log_preimages_length + + revertible.encrypted_log_preimages_length, + unencrypted_log_preimages_length: non_revertible.unencrypted_log_preimages_length + + revertible.unencrypted_log_preimages_length, public_data_update_requests: array_merge( non_revertible.public_data_update_requests, revertible.public_data_update_requests diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data.nr index cfde3348d1b..31c73652ee0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data.nr @@ -1,4 +1,9 @@ -use crate::{abis::{call_request::CallRequest, gas::Gas, side_effect::{SideEffect, SideEffectLinkedToNoteHash}}}; +use crate::{ + abis::{ + call_request::CallRequest, gas::Gas, note_hash::NoteHashContext, nullifier::Nullifier, + side_effect::SideEffect +} +}; use crate::constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, @@ -6,8 +11,8 @@ use crate::constants::{ }; struct PrivateAccumulatedData { - new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], - new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + new_note_hashes: [NoteHashContext; MAX_NEW_NOTE_HASHES_PER_TX], + new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], new_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], encrypted_logs_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr index 222632af328..7f686715f25 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr @@ -7,8 +7,8 @@ use crate::{ private_accumulated_data::PrivateAccumulatedData, public_accumulated_data::PublicAccumulatedData, public_accumulated_data_builder::PublicAccumulatedDataBuilder }, - call_request::CallRequest, public_data_update_request::PublicDataUpdateRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + call_request::CallRequest, note_hash::{NoteHash, NoteHashContext}, nullifier::Nullifier, + public_data_update_request::PublicDataUpdateRequest, side_effect::SideEffect }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, @@ -23,8 +23,8 @@ use crate::{ // .to_combined: KernelCircuitPublicInputs.end // .split_to_public: PublicKernelCircuitPublicInputs.(end,end_non_revertible) struct PrivateAccumulatedDataBuilder { - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, new_l2_to_l1_msgs: BoundedVec, encrypted_logs_hashes: BoundedVec, @@ -66,8 +66,8 @@ impl PrivateAccumulatedDataBuilder { let unencrypted_logs_hash = compute_tx_logs_hash(self.unencrypted_logs_hashes.storage); CombinedAccumulatedData { - new_note_hashes: self.new_note_hashes.storage.map(|n: SideEffect| n.value), - new_nullifiers: self.new_nullifiers.storage.map(|n: SideEffectLinkedToNoteHash| n.value), + new_note_hashes: self.new_note_hashes.storage.map(|n: NoteHashContext| n.value), + new_nullifiers: self.new_nullifiers.storage.map(|n: Nullifier| n.value), new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, encrypted_logs_hash, unencrypted_logs_hash, @@ -87,10 +87,11 @@ impl PrivateAccumulatedDataBuilder { for i in 0..MAX_NEW_NOTE_HASHES_PER_TX { let note_hash = self.new_note_hashes.storage[i]; + let public_note_hash = note_hash.expose_to_public(); if note_hash.counter < min_revertible_side_effect_counter { - non_revertible_builder.new_note_hashes.push(note_hash); + non_revertible_builder.new_note_hashes.push(public_note_hash); } else { - revertible_builder.new_note_hashes.push(note_hash); + revertible_builder.new_note_hashes.push(public_note_hash); } } @@ -146,8 +147,8 @@ mod tests { abis::{ accumulated_data::private_accumulated_data_builder::PrivateAccumulatedDataBuilder, gas::Gas, call_request::CallRequest, caller_context::CallerContext, - public_data_update_request::PublicDataUpdateRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::{NoteHash, NoteHashContext}, nullifier::Nullifier, + public_data_update_request::PublicDataUpdateRequest, side_effect::SideEffect }, address::AztecAddress, utils::arrays::array_eq }; @@ -156,14 +157,14 @@ mod tests { unconstrained fn splits_revertible_and_non_revertible() { let mut builder = PrivateAccumulatedDataBuilder::empty(); - let non_revertible_commitments = [ - SideEffect { value: 1, counter: 1 }, - SideEffect { value: 2, counter: 3 } + let non_revertible_note_hashes = [ + NoteHashContext { value: 1, counter: 1, nullifier_counter: 20 }, + NoteHashContext { value: 2, counter: 3, nullifier_counter: 5 } ]; let non_revertible_nullifiers = [ - SideEffectLinkedToNoteHash { value: 10, note_hash: 1, counter: 2 }, - SideEffectLinkedToNoteHash { value: 20, note_hash: 2, counter: 4 } + Nullifier { value: 10, note_hash: 1, counter: 2 }, + Nullifier { value: 20, note_hash: 2, counter: 4 } ]; let non_revertible_public_stack = [ @@ -183,14 +184,14 @@ mod tests { } ]; - let revertible_commitments = [ - SideEffect { value: 3, counter: 7 }, - SideEffect { value: 4, counter: 10 } + let revertible_note_hashes = [ + NoteHashContext { value: 3, counter: 7, nullifier_counter: 15 }, + NoteHashContext { value: 4, counter: 10, nullifier_counter: 0 } ]; let revertible_nullifiers = [ - SideEffectLinkedToNoteHash { value: 30, note_hash: 3, counter: 8 }, - SideEffectLinkedToNoteHash { value: 40, note_hash: 4, counter: 11 } + Nullifier { value: 30, note_hash: 3, counter: 8 }, + Nullifier { value: 40, note_hash: 4, counter: 11 } ]; let revertible_public_call_stack = [ @@ -203,8 +204,8 @@ mod tests { } ]; - builder.new_note_hashes.extend_from_array(non_revertible_commitments); - builder.new_note_hashes.extend_from_array(revertible_commitments); + builder.new_note_hashes.extend_from_array(non_revertible_note_hashes); + builder.new_note_hashes.extend_from_array(revertible_note_hashes); builder.new_nullifiers.extend_from_array(non_revertible_nullifiers); builder.new_nullifiers.extend_from_array(revertible_nullifiers); @@ -217,11 +218,27 @@ mod tests { let (non_revertible, revertible) = builder.split_to_public(7); - assert(array_eq(non_revertible.new_note_hashes, non_revertible_commitments)); + assert( + array_eq( + non_revertible.new_note_hashes, + [ + NoteHash { value: 1, counter: 0 }, + NoteHash { value: 2, counter: 0 } + ] + ) + ); assert(array_eq(non_revertible.new_nullifiers, non_revertible_nullifiers)); assert(array_eq(non_revertible.public_call_stack, non_revertible_public_stack)); - assert(array_eq(revertible.new_note_hashes, revertible_commitments)); + assert( + array_eq( + revertible.new_note_hashes, + [ + NoteHash { value: 3, counter: 0 }, + NoteHash { value: 4, counter: 0 } + ] + ) + ); assert(array_eq(revertible.new_nullifiers, revertible_nullifiers)); assert(array_eq(revertible.public_call_stack, revertible_public_call_stack)); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data.nr index 595f1aa8193..8a4b4f8c129 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data.nr @@ -1,19 +1,19 @@ use crate::{ abis::{ call_request::CallRequest, public_data_update_request::PublicDataUpdateRequest, gas::Gas, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + note_hash::NoteHash, nullifier::Nullifier, side_effect::SideEffect }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX + MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX }, traits::Empty }; struct PublicAccumulatedData { - new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_TX], - new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], + new_note_hashes: [NoteHash; MAX_NEW_NOTE_HASHES_PER_TX], + new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], new_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], encrypted_logs_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX], @@ -34,8 +34,8 @@ struct PublicAccumulatedData { impl Empty for PublicAccumulatedData { fn empty() -> Self { PublicAccumulatedData { - new_note_hashes: [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_TX], - new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_TX], + new_note_hashes: [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_TX], + new_nullifiers: [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_TX], new_l2_to_l1_msgs: [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX], encrypted_logs_hashes: [SideEffect::empty(); MAX_ENCRYPTED_LOGS_PER_TX], unencrypted_logs_hashes: [SideEffect::empty(); MAX_UNENCRYPTED_LOGS_PER_TX], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data_builder.nr index e66601a3418..dcf35fcf50f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_data_builder.nr @@ -1,20 +1,20 @@ use crate::{ abis::{ gas::Gas, accumulated_data::public_accumulated_data::PublicAccumulatedData, - call_request::CallRequest, public_data_update_request::PublicDataUpdateRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + call_request::CallRequest, note_hash::NoteHash, nullifier::Nullifier, + public_data_update_request::PublicDataUpdateRequest, side_effect::SideEffect }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX + MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX }, traits::Empty }; struct PublicAccumulatedDataBuilder { - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, new_l2_to_l1_msgs: BoundedVec, encrypted_logs_hashes: BoundedVec, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr new file mode 100644 index 00000000000..37dc08fca07 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/note_hash.nr @@ -0,0 +1,128 @@ +use crate::{ + address::AztecAddress, abis::side_effect::{Ordered, OrderedValue}, + constants::{NOTE_HASH_LENGTH, NOTE_HASH_CONTEXT_LENGTH}, traits::{Empty, Serialize, Deserialize} +}; +use dep::std::cmp::Eq; + +struct NoteHash { + value: Field, + counter: u32, +} + +impl Ordered for NoteHash { + fn counter(self) -> u32 { + self.counter + } +} + +impl OrderedValue for NoteHash { + fn value(self) -> Field { + self.value + } + fn counter(self) -> u32 { + self.counter + } +} + +impl Eq for NoteHash { + fn eq(self, other: NoteHash) -> bool { + (self.value == other.value) + & (self.counter == other.counter) + } +} + +impl Empty for NoteHash { + fn empty() -> Self { + NoteHash { + value: 0, + counter: 0, + } + } +} + +impl Serialize for NoteHash { + fn serialize(self) -> [Field; NOTE_HASH_LENGTH] { + [self.value, self.counter as Field] + } +} + +impl Deserialize for NoteHash { + fn deserialize(values: [Field; NOTE_HASH_LENGTH]) -> Self { + Self { + value: values[0], + counter: values[1] as u32, + } + } +} + +impl NoteHash { + pub fn to_context(self, nullifier_counter: u32) -> NoteHashContext { + NoteHashContext { value: self.value, counter: self.counter, nullifier_counter } + } +} + +struct NoteHashContext { + value: Field, + counter: u32, + nullifier_counter: u32, +} + +impl Ordered for NoteHashContext { + fn counter(self) -> u32 { + self.counter + } +} + +impl OrderedValue for NoteHashContext { + fn value(self) -> Field { + self.value + } + fn counter(self) -> u32 { + self.counter + } +} + +impl Eq for NoteHashContext { + fn eq(self, other: NoteHashContext) -> bool { + (self.value == other.value) + & (self.counter == other.counter) + & (self.nullifier_counter == other.nullifier_counter) + } +} + +impl Empty for NoteHashContext { + fn empty() -> Self { + NoteHashContext { + value: 0, + counter: 0, + nullifier_counter: 0, + } + } +} + +impl Serialize for NoteHashContext { + fn serialize(self) -> [Field; NOTE_HASH_CONTEXT_LENGTH] { + [self.value, self.counter as Field, self.nullifier_counter as Field] + } +} + +impl Deserialize for NoteHashContext { + fn deserialize(values: [Field; NOTE_HASH_CONTEXT_LENGTH]) -> Self { + Self { + value: values[0], + counter: values[1] as u32, + nullifier_counter: values[2] as u32, + } + } +} + +impl NoteHashContext { + pub fn to_note_hash(self) -> NoteHash { + NoteHash { value: self.value, counter: self.counter } + } + + pub fn expose_to_public(self) -> NoteHash { + // Hide the actual counter when exposing it to the public kernel. + NoteHash { value: self.value, counter: 0 } + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr new file mode 100644 index 00000000000..27d8a82be59 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier.nr @@ -0,0 +1,59 @@ +use crate::{ + abis::side_effect::{Ordered, OrderedValue}, address::AztecAddress, constants::NULLIFIER_LENGTH, + traits::{Empty, Hash, Serialize, Deserialize} +}; + +struct Nullifier { + value: Field, + counter: u32, + note_hash: Field, +} + +impl Ordered for Nullifier { + fn counter(self) -> u32 { + self.counter + } +} + +impl OrderedValue for Nullifier { + fn value(self) -> Field { + self.value + } + fn counter(self) -> u32 { + self.counter + } +} + +impl Eq for Nullifier { + fn eq(self, other: Nullifier) -> bool { + (self.value == other.value) + & (self.counter == other.counter) + & (self.note_hash == other.note_hash) + } +} + +impl Empty for Nullifier { + fn empty() -> Self { + Nullifier { + value: 0, + counter: 0, + note_hash: 0, + } + } +} + +impl Serialize for Nullifier { + fn serialize(self) -> [Field; NULLIFIER_LENGTH] { + [self.value, self.counter as Field, self.note_hash] + } +} + +impl Deserialize for Nullifier { + fn deserialize(values: [Field; NULLIFIER_LENGTH]) -> Self { + Self { + value: values[0], + counter: values[1] as u32, + note_hash: values[2], + } + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr index 159cfc964d1..cc4d438f4aa 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr @@ -1,8 +1,8 @@ use crate::{ abis::{ call_context::CallContext, max_block_number::MaxBlockNumber, gas_settings::GasSettings, - nullifier_key_validation_request::NullifierKeyValidationRequest, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + nullifier_key_validation_request::NullifierKeyValidationRequest, note_hash::NoteHash, + nullifier::Nullifier, read_request::ReadRequest, side_effect::SideEffect }, constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, @@ -31,8 +31,8 @@ struct PrivateCircuitPublicInputs { nullifier_read_requests: [ReadRequest; MAX_NULLIFIER_READ_REQUESTS_PER_CALL], nullifier_key_validation_requests: [NullifierKeyValidationRequest; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL], - new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_CALL], - new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_CALL], + new_note_hashes: [NoteHash; MAX_NEW_NOTE_HASHES_PER_CALL], + new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_CALL], private_call_stack_hashes: [Field; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL], public_call_stack_hashes: [Field; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], new_l2_to_l1_msgs: [L2ToL1Message; MAX_NEW_L2_TO_L1_MSGS_PER_CALL], @@ -144,8 +144,8 @@ impl Deserialize for PrivateCircuitPublicI note_hash_read_requests: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]), nullifier_read_requests: reader.read_struct_array(ReadRequest::deserialize, [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL]), nullifier_key_validation_requests: reader.read_struct_array(NullifierKeyValidationRequest::deserialize, [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL]), - new_note_hashes: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL]), - new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialize, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), + new_note_hashes: reader.read_struct_array(NoteHash::deserialize, [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL]), + new_nullifiers: reader.read_struct_array(Nullifier::deserialize, [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), private_call_stack_hashes: reader.read_array([0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]), public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]), new_l2_to_l1_msgs: reader.read_struct_array(L2ToL1Message::deserialize, [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL]), @@ -181,8 +181,8 @@ impl Empty for PrivateCircuitPublicInputs { note_hash_read_requests: [SideEffect::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], nullifier_read_requests: [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL], nullifier_key_validation_requests: [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL], - new_note_hashes: [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], - new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL], + new_note_hashes: [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], + new_nullifiers: [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL], private_call_stack_hashes: [0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL], public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], new_l2_to_l1_msgs: [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr index 301e04e65cc..bdd3aa1c570 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr @@ -48,7 +48,7 @@ impl PublicCallStackItem { mod tests { use crate::{ abis::{ - function_data::FunctionData, function_selector::FunctionSelector, + function_data::FunctionData, function_selector::FunctionSelector, note_hash::NoteHash, public_circuit_public_inputs::PublicCircuitPublicInputs, public_call_stack_item::PublicCallStackItem, side_effect::SideEffect }, @@ -61,7 +61,7 @@ mod tests { let function_data = FunctionData { selector: FunctionSelector::from_u32(2), is_private: false }; let mut public_inputs = PublicCircuitPublicInputs::empty(); - public_inputs.new_note_hashes[0] = SideEffect{ + public_inputs.new_note_hashes[0] = NoteHash { value: 1, counter: 0, }; @@ -79,7 +79,7 @@ mod tests { let function_data = FunctionData { selector: FunctionSelector::from_u32(2), is_private: false }; let mut public_inputs = PublicCircuitPublicInputs::empty(); - public_inputs.new_note_hashes[0] = SideEffect{ + public_inputs.new_note_hashes[0] = NoteHash { value: 1, counter: 0, }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr index 53865b83b21..41fadb37de3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr @@ -1,7 +1,7 @@ use crate::{ abis::{ - call_context::CallContext, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash}, gas::Gas, global_variables::GlobalVariables + call_context::CallContext, note_hash::NoteHash, nullifier::Nullifier, read_request::ReadRequest, + side_effect::SideEffect, gas::Gas, global_variables::GlobalVariables }, address::AztecAddress, constants::{ @@ -29,8 +29,8 @@ struct PublicCircuitPublicInputs { // todo: add sideeffect ranges for the input to these hashes public_call_stack_hashes: [Field; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], - new_note_hashes: [SideEffect; MAX_NEW_NOTE_HASHES_PER_CALL], - new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_CALL], + new_note_hashes: [NoteHash; MAX_NEW_NOTE_HASHES_PER_CALL], + new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_CALL], new_l2_to_l1_msgs: [L2ToL1Message; MAX_NEW_L2_TO_L1_MSGS_PER_CALL], start_side_effect_counter: u32, @@ -125,8 +125,8 @@ impl Deserialize for PublicCircuitPublicInp contract_storage_update_requests: reader.read_struct_array(StorageUpdateRequest::deserialize, [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL]), contract_storage_reads: reader.read_struct_array(StorageRead::deserialize, [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL]), public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]), - new_note_hashes: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL]), - new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialize, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), + new_note_hashes: reader.read_struct_array(NoteHash::deserialize, [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL]), + new_nullifiers: reader.read_struct_array(Nullifier::deserialize, [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), new_l2_to_l1_msgs: reader.read_struct_array(L2ToL1Message::deserialize, [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL]), start_side_effect_counter: reader.read() as u32, end_side_effect_counter: reader.read() as u32, @@ -163,8 +163,8 @@ impl Empty for PublicCircuitPublicInputs { contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL], contract_storage_reads: [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL], public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], - new_note_hashes: [SideEffect::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], - new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL], + new_note_hashes: [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], + new_nullifiers: [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL], new_l2_to_l1_msgs: [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL], start_side_effect_counter: 0 as u32, end_side_effect_counter: 0 as u32, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr index 63448cc0ebe..0ae351d803f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/side_effect.nr @@ -1,7 +1,6 @@ use crate::{ address::AztecAddress, constants::GENERATOR_INDEX__SIDE_EFFECT, - traits::{Empty, Hash, Serialize, Deserialize}, - constants::{SIDE_EFFECT_LENGTH, SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH}, + traits::{Empty, Hash, Serialize, Deserialize}, constants::SIDE_EFFECT_LENGTH }; use dep::std::cmp::Eq; @@ -77,66 +76,3 @@ impl Deserialize for SideEffect { } } } - -struct SideEffectLinkedToNoteHash { - value: Field, - note_hash: Field, - counter: u32, -} - -impl Ordered for SideEffectLinkedToNoteHash { - fn counter(self) -> u32 { - self.counter - } -} - -impl OrderedValue for SideEffectLinkedToNoteHash { - fn value(self) -> Field { - self.value - } - fn counter(self) -> u32 { - self.counter - } -} - -impl Eq for SideEffectLinkedToNoteHash { - fn eq(self, side_effect: SideEffectLinkedToNoteHash) -> bool { - (self.value == side_effect.value) - & (self.note_hash == side_effect.note_hash) - & (self.counter == side_effect.counter) - } -} - -impl Empty for SideEffectLinkedToNoteHash { - fn empty() -> Self { - SideEffectLinkedToNoteHash { - value: 0, - note_hash: 0, - counter: 0, - } - } -} - -impl Hash for SideEffectLinkedToNoteHash { - fn hash(self) -> Field { - dep::std::hash::pedersen_hash_with_separator( - self.serialize(), - GENERATOR_INDEX__SIDE_EFFECT) - } -} - -impl Serialize for SideEffectLinkedToNoteHash { - fn serialize(self) -> [Field; SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH] { - [self.value, self.note_hash, self.counter as Field] - } -} - -impl Deserialize for SideEffectLinkedToNoteHash { - fn deserialize(values: [Field; SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH]) -> Self { - Self { - value: values[0], - note_hash: values[1], - counter: values[2] as u32, - } - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 1594f7adf20..61924a73f8e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -158,14 +158,16 @@ global NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 3; global NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_LENGTH = 4; global PARTIAL_STATE_REFERENCE_LENGTH: u64 = 6; global READ_REQUEST_LENGTH = 2; +global NOTE_HASH_LENGTH = 2; +global NOTE_HASH_CONTEXT_LENGTH = 3; +global NULLIFIER_LENGTH = 3; global SIDE_EFFECT_LENGTH = 2; -global SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH = 3; global STATE_REFERENCE_LENGTH: u64 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + PARTIAL_STATE_REFERENCE_LENGTH; global TX_CONTEXT_LENGTH: u64 = 2 + GAS_SETTINGS_LENGTH; global TX_REQUEST_LENGTH: u64 = 2 + TX_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH; global HEADER_LENGTH: u64 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + CONTENT_COMMITMENT_LENGTH + STATE_REFERENCE_LENGTH + GLOBAL_VARIABLES_LENGTH; -global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 3 + MAX_BLOCK_NUMBER_LENGTH + (SIDE_EFFECT_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL) + (SIDE_EFFECT_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_ENCRYPTED_LOGS_PER_CALL) + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 2 + HEADER_LENGTH + TX_CONTEXT_LENGTH; -global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 2 + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (SIDE_EFFECT_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 1 + HEADER_LENGTH + GLOBAL_VARIABLES_LENGTH + AZTEC_ADDRESS_LENGTH + /* revert_code */ 1 + 2 * GAS_LENGTH + /* transaction_fee */ 1; +global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 3 + MAX_BLOCK_NUMBER_LENGTH + (SIDE_EFFECT_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL) + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_ENCRYPTED_LOGS_PER_CALL) + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 2 + HEADER_LENGTH + TX_CONTEXT_LENGTH; +global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 2 + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 1 + HEADER_LENGTH + GLOBAL_VARIABLES_LENGTH + AZTEC_ADDRESS_LENGTH + /* revert_code */ 1 + 2 * GAS_LENGTH + /* transaction_fee */ 1; global PRIVATE_CALL_STACK_ITEM_LENGTH: u64 = AZTEC_ADDRESS_LENGTH + FUNCTION_DATA_LENGTH + PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH; global ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH: u64 = 2 + FUNCTION_DATA_LENGTH + CALL_CONTEXT_LENGTH; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/mocked.nr b/noir-projects/noir-protocol-circuits/crates/types/src/mocked.nr index d804d99edc5..43567eadd7a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/mocked.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/mocked.nr @@ -2,7 +2,6 @@ // they need the proof system. use crate::traits::Empty; - struct AggregationObject{} struct Proof{} @@ -35,3 +34,8 @@ pub fn verify_previous_kernel_state( ) -> (bool, AggregationObject) { (true, AggregationObject {}) } + +// Called by private kernel init circuit which has no previous kernel proof. +pub fn verify_private_function_proof(_proof: Proof) -> bool { + true +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 4d02ae8cea0..4cf0e8f7596 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -2,16 +2,14 @@ use crate::{ abis::{ gas::Gas, gas_settings::GasSettings, call_context::CallContext, call_request::{CallerContext, CallRequest}, - accumulated_data::{ - CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, - PublicAccumulatedData, PublicAccumulatedDataBuilder -}, + accumulated_data::{CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PublicAccumulatedData}, global_variables::GlobalVariables, combined_constant_data::CombinedConstantData, kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, kernel_data::{PrivateKernelData, PublicKernelData, KernelData}, max_block_number::MaxBlockNumber, + note_hash::NoteHashContext, nullifier::Nullifier, nullifier_key_validation_request::NullifierKeyValidationRequestContext, public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, - read_request::ReadRequestContext, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, + read_request::ReadRequestContext, side_effect::SideEffect, validation_requests::{ValidationRequests, ValidationRequestsBuilder} }, address::AztecAddress, @@ -38,8 +36,8 @@ struct FixtureBuilder { global_variables: GlobalVariables, // Accumulated data. - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, new_l2_to_l1_msgs: BoundedVec, encrypted_logs_hashes: BoundedVec, unencrypted_logs_hashes: BoundedVec, @@ -143,25 +141,24 @@ impl FixtureBuilder { } pub fn to_public_accumulated_data(self) -> PublicAccumulatedData { - let public_inputs = PublicAccumulatedDataBuilder { - new_note_hashes: self.new_note_hashes, - new_nullifiers: self.new_nullifiers, - new_l2_to_l1_msgs: self.new_l2_to_l1_msgs, - encrypted_logs_hashes: self.encrypted_logs_hashes, - unencrypted_logs_hashes: self.unencrypted_logs_hashes, + PublicAccumulatedData { + new_note_hashes: self.new_note_hashes.storage.map(|n: NoteHashContext| n.to_note_hash()), + new_nullifiers: self.new_nullifiers.storage, + new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, + encrypted_logs_hashes: self.encrypted_logs_hashes.storage, + unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage, encrypted_log_preimages_length: self.encrypted_log_preimages_length, unencrypted_log_preimages_length: self.unencrypted_log_preimages_length, - public_data_update_requests: self.public_data_update_requests, - public_call_stack: self.public_call_stack, + public_data_update_requests: self.public_data_update_requests.storage, + public_call_stack: self.public_call_stack.storage, gas_used: self.gas_used - }; - public_inputs.finish() + } } pub fn to_combined_accumulated_data(self) -> CombinedAccumulatedData { CombinedAccumulatedData { - new_note_hashes: self.new_note_hashes.storage.map(|n: SideEffect| n.value), - new_nullifiers: self.new_nullifiers.storage.map(|n: SideEffectLinkedToNoteHash| n.value), + new_note_hashes: self.new_note_hashes.storage.map(|n: NoteHashContext| n.value), + new_nullifiers: self.new_nullifiers.storage.map(|n: Nullifier| n.value), new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, encrypted_logs_hash: self.encrypted_logs_hash, unencrypted_logs_hash: self.unencrypted_logs_hash, @@ -253,21 +250,23 @@ impl FixtureBuilder { KernelData { public_inputs, proof: self.proof, vk: self.vk, vk_index: self.vk_index, vk_path: self.vk_path } } + pub fn add_new_note_hash(&mut self, value: Field) { + self.new_note_hashes.push(NoteHashContext { value, counter: self.next_counter(), nullifier_counter: 0 }); + } + pub fn append_new_note_hashes(&mut self, num_new_note_hashes: u64) { let mocked_value_offset = self.new_note_hashes.len() + 1; for i in 0..MAX_NEW_NOTE_HASHES_PER_TX { if i < num_new_note_hashes { // The empty value is its index + 1. - self.new_note_hashes.push( - SideEffect { value: (i + mocked_value_offset) as Field, counter: self.next_counter() } - ); + self.add_new_note_hash((i + mocked_value_offset) as Field); } } } pub fn add_nullifier(&mut self, unsiloed_nullifier: Field) { let value = silo_nullifier(self.storage_contract_address, unsiloed_nullifier); - self.new_nullifiers.push(SideEffectLinkedToNoteHash { value, note_hash: 0, counter: self.next_counter() }); + self.new_nullifiers.push(Nullifier { value, note_hash: 0, counter: self.next_counter() }); } pub fn append_new_nullifiers(&mut self, num_extra_nullifier: u64) { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr index f3de910442d..5aec252fc56 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr @@ -1,9 +1,10 @@ use crate::{ abis::{ call_context::CallContext, gas_settings::GasSettings, gas::Gas, max_block_number::MaxBlockNumber, + note_hash::NoteHash, nullifier::Nullifier, nullifier_key_validation_request::NullifierKeyValidationRequest, private_circuit_public_inputs::PrivateCircuitPublicInputs, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + side_effect::SideEffect }, address::{AztecAddress, compute_initialization_hash}, header::Header, messaging::l2_to_l1_message::L2ToL1Message, tests::fixtures, transaction::tx_context::TxContext @@ -33,8 +34,8 @@ struct PrivateCircuitPublicInputsBuilder { nullifier_read_requests: BoundedVec, nullifier_key_validation_requests: BoundedVec, - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, private_call_stack_hashes: BoundedVec, public_call_stack_hashes: BoundedVec, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr index db56f3b0852..994c8082615 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr @@ -1,8 +1,8 @@ use crate::{ abis::{ - gas::Gas, call_context::CallContext, public_circuit_public_inputs::PublicCircuitPublicInputs, - read_request::ReadRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, - global_variables::GlobalVariables + gas::Gas, call_context::CallContext, note_hash::NoteHash, nullifier::Nullifier, + public_circuit_public_inputs::PublicCircuitPublicInputs, read_request::ReadRequest, + side_effect::SideEffect, global_variables::GlobalVariables }, address::AztecAddress, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, header::Header, @@ -27,8 +27,8 @@ struct PublicCircuitPublicInputsBuilder { contract_storage_update_requests: BoundedVec, contract_storage_reads: BoundedVec, public_call_stack_hashes: BoundedVec, - new_note_hashes: BoundedVec, - new_nullifiers: BoundedVec, + new_note_hashes: BoundedVec, + new_nullifiers: BoundedVec, new_l2_to_l1_msgs: BoundedVec, start_side_effect_counter: u32, end_side_effect_counter: u32, diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 3d5c1092bd2..8adb5686257 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -3,12 +3,12 @@ import { CallRequest, GasSettings, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, + Nullifier, PartialPrivateTailPublicInputsForPublic, PrivateKernelTailCircuitPublicInputs, Proof, type PublicCallRequest, SideEffect, - SideEffectLinkedToNoteHash, computeContractClassId, getContractClassFromArtifact, } from '@aztec/circuits.js'; @@ -63,7 +63,7 @@ export const mockTx = ( const isForPublic = totalPublicCallRequests > 0; const data = PrivateKernelTailCircuitPublicInputs.empty(); - const firstNullifier = new SideEffectLinkedToNoteHash(new Fr(seed + 1), new Fr(seed + 2), Fr.ZERO); + const firstNullifier = new Nullifier(new Fr(seed + 1), 0, Fr.ZERO); const encryptedLogs = hasLogs ? EncryptedTxL2Logs.random(2, 3) : EncryptedTxL2Logs.empty(); // 2 priv function invocations creating 3 encrypted logs each const unencryptedLogs = hasLogs ? UnencryptedTxL2Logs.random(2, 1) : UnencryptedTxL2Logs.empty(); // 2 priv function invocations creating 1 unencrypted log each data.constants.txContext.gasSettings = GasSettings.default(); diff --git a/yarn-project/circuits.js/src/hash/__snapshots__/hash.test.ts.snap b/yarn-project/circuits.js/src/hash/__snapshots__/hash.test.ts.snap index 7ccffcf9bec..a58b637a959 100644 --- a/yarn-project/circuits.js/src/hash/__snapshots__/hash.test.ts.snap +++ b/yarn-project/circuits.js/src/hash/__snapshots__/hash.test.ts.snap @@ -1,9 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`hash Computes an empty nullifier hash 1`] = `"0x066e6cdc4a6ba5e4781deda650b0be6c12f975f064fc38df72c1060716759b17"`; - -exports[`hash Computes an empty sideeffect hash 1`] = `"0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed"`; - exports[`hash Var args hash matches noir 1`] = `Fr<0x05a1023fef839ac88731f49ae983e172c1b600a3c8f3393ad0ac25d819ac0f0f>`; exports[`hash compute secret message hash 1`] = `Fr<0x0dc06f2167e2cd19adf738d1f38469d7f8bff1e26b029816e8230bcd6ab6332e>`; diff --git a/yarn-project/circuits.js/src/hash/hash.test.ts b/yarn-project/circuits.js/src/hash/hash.test.ts index 57abd049674..9c148367c66 100644 --- a/yarn-project/circuits.js/src/hash/hash.test.ts +++ b/yarn-project/circuits.js/src/hash/hash.test.ts @@ -1,12 +1,10 @@ import { times } from '@aztec/foundation/collection'; import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing'; -import { AztecAddress, Fr, SideEffect, SideEffectLinkedToNoteHash } from '../index.js'; +import { AztecAddress, Fr } from '../index.js'; import { makeAztecAddress } from '../tests/factories.js'; import { computeCommitmentNonce, - computeCommitmentsHash, - computeNullifierHash, computePublicDataTreeLeafSlot, computePublicDataTreeValue, computeSecretHash, @@ -83,19 +81,6 @@ describe('hash', () => { expect(hash).toMatchSnapshot(); }); - it('Computes an empty nullifier hash ', () => { - const emptyNull = SideEffectLinkedToNoteHash.empty(); - - const emptyHash = computeNullifierHash(emptyNull).toString(); - expect(emptyHash).toMatchSnapshot(); - }); - - it('Computes an empty sideeffect hash ', () => { - const emptySideEffect = SideEffect.empty(); - const emptyHash = computeCommitmentsHash(emptySideEffect).toString(); - expect(emptyHash).toMatchSnapshot(); - }); - it('Var args hash matches noir', () => { const args = times(800, i => new Fr(i)); const res = computeVarArgsHash(args); diff --git a/yarn-project/circuits.js/src/hash/hash.ts b/yarn-project/circuits.js/src/hash/hash.ts index f2a82eac89b..6127573c8c9 100644 --- a/yarn-project/circuits.js/src/hash/hash.ts +++ b/yarn-project/circuits.js/src/hash/hash.ts @@ -8,7 +8,7 @@ import { numToUInt8, numToUInt16BE, numToUInt32BE } from '@aztec/foundation/seri import chunk from 'lodash.chunk'; import { ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, GeneratorIndex } from '../constants.gen.js'; -import { type SideEffect, type SideEffectLinkedToNoteHash, VerificationKey } from '../structs/index.js'; +import { VerificationKey } from '../structs/index.js'; /** * Computes a hash of a given verification key. @@ -145,14 +145,6 @@ export function computeVarArgsHash(args: Fr[]) { return pedersenHash(chunksHashes, GeneratorIndex.FUNCTION_ARGS); } -export function computeCommitmentsHash(input: SideEffect) { - return pedersenHash([input.value, input.counter], GeneratorIndex.SIDE_EFFECT); -} - -export function computeNullifierHash(input: SideEffectLinkedToNoteHash) { - return pedersenHash([input.value, input.noteHash, input.counter], GeneratorIndex.SIDE_EFFECT); -} - /** * Computes a hash of a secret. * @dev This function is used to generate secrets for the L1 to L2 message flow and for the TransparentNote. diff --git a/yarn-project/circuits.js/src/hints/build_hints.test.ts b/yarn-project/circuits.js/src/hints/build_nullifier_non_existent_read_request_hints.test.ts similarity index 51% rename from yarn-project/circuits.js/src/hints/build_hints.test.ts rename to yarn-project/circuits.js/src/hints/build_nullifier_non_existent_read_request_hints.test.ts index 5df210ca1e8..7d4040e35c8 100644 --- a/yarn-project/circuits.js/src/hints/build_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_nullifier_non_existent_read_request_hints.test.ts @@ -2,116 +2,11 @@ import { makeTuple } from '@aztec/foundation/array'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; -import { type Tuple } from '@aztec/foundation/serialize'; import { MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX } from '../constants.gen.js'; import { siloNullifier } from '../hash/index.js'; -import { - NullifierNonExistentReadRequestHintsBuilder, - type NullifierReadRequestHints, - NullifierReadRequestHintsBuilder, - PendingReadHint, - ReadRequestContext, - ReadRequestState, - ReadRequestStatus, - SettledReadHint, - SideEffectLinkedToNoteHash, -} from '../structs/index.js'; -import { buildNullifierNonExistentReadRequestHints, buildNullifierReadRequestHints } from './build_hints.js'; - -describe('buildNullifierReadRequestHints', () => { - const contractAddress = AztecAddress.random(); - const settledNullifierInnerValue = 99999; - const oracle = { - getNullifierMembershipWitness: () => ({ membershipWitness: {}, leafPreimage: {} } as any), - }; - let nullifierReadRequests: Tuple; - let nullifiers: Tuple; - let expectedHints: NullifierReadRequestHints; - let numReadRequests = 0; - let numPendingReads = 0; - let numSettledReads = 0; - - const innerNullifier = (index: number) => index + 1; - - const makeReadRequest = (value: number, counter = 2) => - new ReadRequestContext(new Fr(value), counter, contractAddress); - - function makeNullifier(value: number, counter = 1) { - const siloedValue = siloNullifier(contractAddress, new Fr(value)); - return new SideEffectLinkedToNoteHash(siloedValue, new Fr(0), new Fr(counter)); - } - - const readPendingNullifier = ({ - nullifierIndex, - readRequestIndex = numReadRequests, - hintIndex = numPendingReads, - }: { - nullifierIndex: number; - readRequestIndex?: number; - hintIndex?: number; - }) => { - nullifierReadRequests[readRequestIndex] = makeReadRequest(innerNullifier(nullifierIndex)); - expectedHints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus(ReadRequestState.PENDING, hintIndex); - expectedHints.pendingReadHints[hintIndex] = new PendingReadHint(readRequestIndex, nullifierIndex); - numReadRequests++; - numPendingReads++; - }; - - const readSettledNullifier = ({ - readRequestIndex = numReadRequests, - hintIndex = numSettledReads, - }: { - readRequestIndex?: number; - hintIndex?: number; - } = {}) => { - nullifierReadRequests[readRequestIndex] = makeReadRequest(settledNullifierInnerValue); - expectedHints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus(ReadRequestState.SETTLED, hintIndex); - expectedHints.settledReadHints[hintIndex] = new SettledReadHint(readRequestIndex, {} as any, {} as any); - numReadRequests++; - numSettledReads++; - }; - - const buildHints = () => buildNullifierReadRequestHints(oracle, nullifierReadRequests, nullifiers); - - beforeEach(() => { - nullifierReadRequests = makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext.empty); - nullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, i => makeNullifier(innerNullifier(i))); - expectedHints = NullifierReadRequestHintsBuilder.empty(); - numReadRequests = 0; - numPendingReads = 0; - numSettledReads = 0; - }); - - it('builds empty hints', async () => { - const hints = await buildHints(); - expect(hints).toEqual(expectedHints); - }); - - it('builds hints for pending nullifier read requests', async () => { - readPendingNullifier({ nullifierIndex: 2 }); - readPendingNullifier({ nullifierIndex: 1 }); - const hints = await buildHints(); - expect(hints).toEqual(expectedHints); - }); - - it('builds hints for settled nullifier read requests', async () => { - readSettledNullifier(); - readSettledNullifier(); - const hints = await buildHints(); - expect(hints).toEqual(expectedHints); - }); - - it('builds hints for mixed pending and settled nullifier read requests', async () => { - readPendingNullifier({ nullifierIndex: 2 }); - readSettledNullifier(); - readSettledNullifier(); - readPendingNullifier({ nullifierIndex: 1 }); - readPendingNullifier({ nullifierIndex: 1 }); - const hints = await buildHints(); - expect(hints).toEqual(expectedHints); - }); -}); +import { Nullifier, NullifierNonExistentReadRequestHintsBuilder, ReadRequestContext } from '../structs/index.js'; +import { buildNullifierNonExistentReadRequestHints } from './build_nullifier_non_existent_read_request_hints.js'; describe('buildNullifierNonExistentReadRequestHints', () => { const contractAddress = AztecAddress.random(); @@ -119,7 +14,7 @@ describe('buildNullifierNonExistentReadRequestHints', () => { getLowNullifierMembershipWitness: () => ({ membershipWitness: {}, leafPreimage: {} } as any), }; const nonExistentReadRequests = makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext.empty); - let nullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash.empty); + let nullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Nullifier.empty); const innerNullifier = (index: number) => index + 1; @@ -128,7 +23,7 @@ describe('buildNullifierNonExistentReadRequestHints', () => { const makeNullifier = (value: number, counter = 1) => { const siloedValue = siloNullifier(contractAddress, new Fr(value)); - return new SideEffectLinkedToNoteHash(siloedValue, new Fr(0), new Fr(counter)); + return new Nullifier(siloedValue, 0, new Fr(counter)); }; interface TestNullifier { @@ -138,7 +33,7 @@ describe('buildNullifierNonExistentReadRequestHints', () => { const populateNullifiers = (numNullifiers = MAX_NEW_NULLIFIERS_PER_TX) => { nullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, i => - i < numNullifiers ? makeNullifier(innerNullifier(i)) : SideEffectLinkedToNoteHash.empty(), + i < numNullifiers ? makeNullifier(innerNullifier(i)) : Nullifier.empty(), ); }; @@ -185,7 +80,7 @@ describe('buildNullifierNonExistentReadRequestHints', () => { // The first half contains sorted values. for (let i = 0; i < numNonEmptyNullifiers - 1; ++i) { - expect(sortedPendingValues[i]).not.toEqual(SideEffectLinkedToNoteHash.empty()); + expect(sortedPendingValues[i]).not.toEqual(Nullifier.empty()); expect(sortedPendingValues[i].value.lt(sortedPendingValues[i + 1].value)).toBe(true); } for (let i = 0; i < numNonEmptyNullifiers; ++i) { @@ -195,7 +90,7 @@ describe('buildNullifierNonExistentReadRequestHints', () => { // The second half is empty. for (let i = numNonEmptyNullifiers; i < sortedPendingValues.length; ++i) { - expect(sortedPendingValues[i]).toEqual(SideEffectLinkedToNoteHash.empty()); + expect(sortedPendingValues[i]).toEqual(Nullifier.empty()); } for (let i = numNonEmptyNullifiers; i < sortedPendingValueHints.length; ++i) { expect(sortedPendingValueHints[i]).toBe(0); @@ -217,7 +112,7 @@ describe('buildNullifierNonExistentReadRequestHints', () => { nonExistentReadRequests[2] = makeReadRequest(minNullifier.value); nullifiers = padArrayEnd( sortedNullifiers.map(n => makeNullifier(n.value)), - SideEffectLinkedToNoteHash.empty(), + Nullifier.empty(), MAX_NEW_NULLIFIERS_PER_TX, ); diff --git a/yarn-project/circuits.js/src/hints/build_hints.ts b/yarn-project/circuits.js/src/hints/build_nullifier_non_existent_read_request_hints.ts similarity index 62% rename from yarn-project/circuits.js/src/hints/build_hints.ts rename to yarn-project/circuits.js/src/hints/build_nullifier_non_existent_read_request_hints.ts index 302ed3a14f9..c2f65701402 100644 --- a/yarn-project/circuits.js/src/hints/build_hints.ts +++ b/yarn-project/circuits.js/src/hints/build_nullifier_non_existent_read_request_hints.ts @@ -6,15 +6,13 @@ import { type IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { MAX_NEW_NULLIFIERS_PER_TX, type MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, - type MAX_NULLIFIER_READ_REQUESTS_PER_TX, type NULLIFIER_TREE_HEIGHT, } from '../constants.gen.js'; import { siloNullifier } from '../hash/index.js'; +import { Nullifier } from '../structs/index.js'; import { type MembershipWitness } from '../structs/membership_witness.js'; import { NullifierNonExistentReadRequestHintsBuilder } from '../structs/non_existent_read_request_hints.js'; import { type ReadRequestContext } from '../structs/read_request.js'; -import { NullifierReadRequestHintsBuilder } from '../structs/read_request_hints.js'; -import { SideEffectLinkedToNoteHash } from '../structs/side_effects.js'; import { countAccumulatedItems } from '../utils/index.js'; interface NullifierMembershipWitnessWithPreimage { @@ -22,48 +20,14 @@ interface NullifierMembershipWitnessWithPreimage { leafPreimage: IndexedTreeLeafPreimage; } -export async function buildNullifierReadRequestHints( - oracle: { - getNullifierMembershipWitness(nullifier: Fr): Promise; - }, - nullifierReadRequests: Tuple, - nullifiers: Tuple, -) { - const builder = new NullifierReadRequestHintsBuilder(); - - const numReadRequests = countAccumulatedItems(nullifierReadRequests); - - const nullifierIndexMap: Map = new Map(); - nullifiers.forEach((n, i) => nullifierIndexMap.set(n.value.toBigInt(), i)); - - for (let i = 0; i < numReadRequests; ++i) { - const readRequest = nullifierReadRequests[i]; - // TODO - Should be comparing un-siloed values and contract addresses. - const value = siloNullifier(readRequest.contractAddress, readRequest.value); - - const pendingValueIndex = nullifierIndexMap.get(value.toBigInt()); - if (pendingValueIndex !== undefined) { - builder.addPendingReadRequest(i, pendingValueIndex); - } else { - const membershipWitnessWithPreimage = await oracle.getNullifierMembershipWitness(value); - builder.addSettledReadRequest( - i, - membershipWitnessWithPreimage.membershipWitness, - membershipWitnessWithPreimage.leafPreimage, - ); - } - } - return builder.toHints(); -} - interface SortedResult { sortedValues: Tuple; sortedIndexHints: Tuple; } function sortNullifiersByValues( - nullifiers: Tuple, -): SortedResult { + nullifiers: Tuple, +): SortedResult { const numNullifiers = countAccumulatedItems(nullifiers); const sorted = nullifiers .slice(0, numNullifiers) @@ -78,7 +42,7 @@ function sortNullifiersByValues( return { sortedValues: padArrayEnd( sorted.map(s => s.nullifier), - SideEffectLinkedToNoteHash.empty(), + Nullifier.empty(), MAX_NEW_NULLIFIERS_PER_TX, ), sortedIndexHints: padArrayEnd(sortedIndexHints, 0, MAX_NEW_NULLIFIERS_PER_TX), @@ -90,7 +54,7 @@ export async function buildNullifierNonExistentReadRequestHints( getLowNullifierMembershipWitness(nullifier: Fr): Promise; }, nullifierNonExistentReadRequests: Tuple, - pendingNullifiers: Tuple, + pendingNullifiers: Tuple, ) { const { sortedValues, sortedIndexHints } = sortNullifiersByValues(pendingNullifiers); diff --git a/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.test.ts b/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.test.ts new file mode 100644 index 00000000000..7bc21c9176e --- /dev/null +++ b/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.test.ts @@ -0,0 +1,112 @@ +import { makeTuple } from '@aztec/foundation/array'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr } from '@aztec/foundation/fields'; +import { type Tuple } from '@aztec/foundation/serialize'; + +import { MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX } from '../constants.gen.js'; +import { siloNullifier } from '../hash/index.js'; +import { + Nullifier, + type NullifierReadRequestHints, + NullifierReadRequestHintsBuilder, + PendingReadHint, + ReadRequestContext, + ReadRequestState, + ReadRequestStatus, + SettledReadHint, +} from '../structs/index.js'; +import { buildNullifierReadRequestHints } from './build_nullifier_read_request_hints.js'; + +describe('buildNullifierReadRequestHints', () => { + const contractAddress = AztecAddress.random(); + const settledNullifierInnerValue = 99999; + const oracle = { + getNullifierMembershipWitness: () => ({ membershipWitness: {}, leafPreimage: {} } as any), + }; + let nullifierReadRequests: Tuple; + let nullifiers: Tuple; + let expectedHints: NullifierReadRequestHints; + let numReadRequests = 0; + let numPendingReads = 0; + let numSettledReads = 0; + + const innerNullifier = (index: number) => index + 1; + + const makeReadRequest = (value: number, counter = 2) => + new ReadRequestContext(new Fr(value), counter, contractAddress); + + function makeNullifier(value: number, counter = 1) { + const siloedValue = siloNullifier(contractAddress, new Fr(value)); + return new Nullifier(siloedValue, 0, new Fr(counter)); + } + + const readPendingNullifier = ({ + nullifierIndex, + readRequestIndex = numReadRequests, + hintIndex = numPendingReads, + }: { + nullifierIndex: number; + readRequestIndex?: number; + hintIndex?: number; + }) => { + nullifierReadRequests[readRequestIndex] = makeReadRequest(innerNullifier(nullifierIndex)); + expectedHints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus(ReadRequestState.PENDING, hintIndex); + expectedHints.pendingReadHints[hintIndex] = new PendingReadHint(readRequestIndex, nullifierIndex); + numReadRequests++; + numPendingReads++; + }; + + const readSettledNullifier = ({ + readRequestIndex = numReadRequests, + hintIndex = numSettledReads, + }: { + readRequestIndex?: number; + hintIndex?: number; + } = {}) => { + nullifierReadRequests[readRequestIndex] = makeReadRequest(settledNullifierInnerValue); + expectedHints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus(ReadRequestState.SETTLED, hintIndex); + expectedHints.settledReadHints[hintIndex] = new SettledReadHint(readRequestIndex, {} as any, {} as any); + numReadRequests++; + numSettledReads++; + }; + + const buildHints = () => buildNullifierReadRequestHints(oracle, nullifierReadRequests, nullifiers); + + beforeEach(() => { + nullifierReadRequests = makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext.empty); + nullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, i => makeNullifier(innerNullifier(i))); + expectedHints = NullifierReadRequestHintsBuilder.empty(); + numReadRequests = 0; + numPendingReads = 0; + numSettledReads = 0; + }); + + it('builds empty hints', async () => { + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('builds hints for pending nullifier read requests', async () => { + readPendingNullifier({ nullifierIndex: 2 }); + readPendingNullifier({ nullifierIndex: 1 }); + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('builds hints for settled nullifier read requests', async () => { + readSettledNullifier(); + readSettledNullifier(); + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('builds hints for mixed pending and settled nullifier read requests', async () => { + readPendingNullifier({ nullifierIndex: 2 }); + readSettledNullifier(); + readSettledNullifier(); + readPendingNullifier({ nullifierIndex: 1 }); + readPendingNullifier({ nullifierIndex: 1 }); + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); +}); diff --git a/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts b/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts new file mode 100644 index 00000000000..0f758e51f80 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/build_nullifier_read_request_hints.ts @@ -0,0 +1,54 @@ +import { type Fr } from '@aztec/foundation/fields'; +import { type Tuple } from '@aztec/foundation/serialize'; +import { type IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; + +import { + type MAX_NEW_NULLIFIERS_PER_TX, + type MAX_NULLIFIER_READ_REQUESTS_PER_TX, + type NULLIFIER_TREE_HEIGHT, +} from '../constants.gen.js'; +import { siloNullifier } from '../hash/index.js'; +import { type Nullifier } from '../structs/index.js'; +import { type MembershipWitness } from '../structs/membership_witness.js'; +import { type ReadRequestContext } from '../structs/read_request.js'; +import { NullifierReadRequestHintsBuilder } from '../structs/read_request_hints.js'; +import { countAccumulatedItems } from '../utils/index.js'; + +interface NullifierMembershipWitnessWithPreimage { + membershipWitness: MembershipWitness; + leafPreimage: IndexedTreeLeafPreimage; +} + +export async function buildNullifierReadRequestHints( + oracle: { + getNullifierMembershipWitness(nullifier: Fr): Promise; + }, + nullifierReadRequests: Tuple, + nullifiers: Tuple, +) { + const builder = new NullifierReadRequestHintsBuilder(); + + const numReadRequests = countAccumulatedItems(nullifierReadRequests); + + const nullifierIndexMap: Map = new Map(); + nullifiers.forEach((n, i) => nullifierIndexMap.set(n.value.toBigInt(), i)); + + for (let i = 0; i < numReadRequests; ++i) { + const readRequest = nullifierReadRequests[i]; + // TODO - Should be comparing un-siloed values and contract addresses. + const value = siloNullifier(readRequest.contractAddress, readRequest.value); + + const pendingValueIndex = nullifierIndexMap.get(value.toBigInt()); + if (pendingValueIndex !== undefined) { + builder.addPendingReadRequest(i, pendingValueIndex); + } else { + const membershipWitnessWithPreimage = await oracle.getNullifierMembershipWitness(value); + builder.addSettledReadRequest( + i, + membershipWitnessWithPreimage.membershipWitness, + membershipWitnessWithPreimage.leafPreimage, + ); + } + } + return builder.toHints(); +} diff --git a/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts b/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts new file mode 100644 index 00000000000..a0d4e3fee60 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/build_transient_data_hints.test.ts @@ -0,0 +1,38 @@ +import { Fr, NoteHashContext, Nullifier } from '@aztec/circuits.js'; + +import { buildTransientDataHints } from './build_transient_data_hints.js'; + +describe('buildTransientDataHints', () => { + let noteHashes: NoteHashContext[]; + let nullifiers: Nullifier[]; + + beforeEach(() => { + noteHashes = [ + new NoteHashContext(new Fr(11), 100, 700), + new NoteHashContext(new Fr(22), 200, 0), + new NoteHashContext(new Fr(33), 300, 500), + ]; + nullifiers = [ + new Nullifier(new Fr(44), 400, new Fr(0)), + new Nullifier(new Fr(55), 500, new Fr(33)), + new Nullifier(new Fr(66), 600, new Fr(0)), + new Nullifier(new Fr(77), 700, new Fr(11)), + ]; + }); + + it('builds index hints that link transient note hashes and nullifiers', () => { + const [nullifierIndexes, noteHashIndexes] = buildTransientDataHints(noteHashes, nullifiers); + expect(nullifierIndexes).toEqual([3, 4, 1]); + expect(noteHashIndexes).toEqual([3, 2, 3, 0]); + }); + + it('throws if no matching nullifier', () => { + noteHashes[0].nullifierCounter = 450; + expect(() => buildTransientDataHints(noteHashes, nullifiers)).toThrow('Unknown nullifier counter.'); + }); + + it('throws if note hash does not match', () => { + nullifiers[1].noteHash = new Fr(11); + expect(() => buildTransientDataHints(noteHashes, nullifiers)).toThrow('Hinted note hash does not match.'); + }); +}); diff --git a/yarn-project/circuits.js/src/hints/build_transient_data_hints.ts b/yarn-project/circuits.js/src/hints/build_transient_data_hints.ts new file mode 100644 index 00000000000..bee36948fd7 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/build_transient_data_hints.ts @@ -0,0 +1,44 @@ +import { type NoteHashContext, type Nullifier, countAccumulatedItems } from '@aztec/circuits.js'; +import { makeTuple } from '@aztec/foundation/array'; +import { type Tuple } from '@aztec/foundation/serialize'; + +export function buildTransientDataHints( + noteHashes: Tuple, + nullifiers: Tuple, + noteHashesLength: NOTE_HASHES_LEN = noteHashes.length as NOTE_HASHES_LEN, + nullifiersLength: NULLIFIERS_LEN = nullifiers.length as NULLIFIERS_LEN, +): [Tuple, Tuple] { + const nullifierIndexMap: Map = new Map(); + nullifiers.forEach((n, i) => nullifierIndexMap.set(n.counter, i)); + + const nullifierIndexesForNoteHashes: Tuple = makeTuple( + noteHashesLength, + () => nullifiersLength, + ); + + const noteHashIndexesForNullifiers: Tuple = makeTuple( + nullifiersLength, + () => noteHashesLength, + ); + + const numNoteHashes = countAccumulatedItems(noteHashes); + for (let i = 0; i < numNoteHashes; i++) { + const noteHash = noteHashes[i]; + if (noteHash.nullifierCounter > 0) { + const nullifierIndex = nullifierIndexMap.get(noteHash.nullifierCounter); + if (nullifierIndex === undefined) { + throw new Error('Unknown nullifier counter.'); + } + + const nullifier = nullifiers[nullifierIndex]; + if (!nullifier.noteHash.equals(noteHash.value)) { + throw new Error('Hinted note hash does not match.'); + } + + nullifierIndexesForNoteHashes[i] = nullifierIndex; + noteHashIndexesForNullifiers[nullifierIndex] = i; + } + } + + return [nullifierIndexesForNoteHashes, noteHashIndexesForNullifiers]; +} diff --git a/yarn-project/circuits.js/src/hints/index.ts b/yarn-project/circuits.js/src/hints/index.ts index d9378ed85b5..d1adeb2118a 100644 --- a/yarn-project/circuits.js/src/hints/index.ts +++ b/yarn-project/circuits.js/src/hints/index.ts @@ -1,3 +1,5 @@ -export * from './build_hints.js'; +export * from './build_nullifier_non_existent_read_request_hints.js'; +export * from './build_nullifier_read_request_hints.js'; export * from './build_public_data_hints.js'; export * from './build_public_data_read_request_hints.js'; +export * from './build_transient_data_hints.js'; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap index 0299196996c..0a6b285dc0c 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap @@ -2,4 +2,4 @@ exports[`PrivateCallStackItem computes empty item hash 1`] = `Fr<0x17fd6ffcb3394b845069dc87e055c37ac50599f274130fac69c6fe919bfe382e>`; -exports[`PrivateCallStackItem computes hash 1`] = `Fr<0x1e08d7680651e7a3d41fa303b981be282e0b019921978866ce7b634ac520b494>`; +exports[`PrivateCallStackItem computes hash 1`] = `Fr<0x07e0e054d39be2aab72d74cc72ef8adc61016744fd985c6736e14e710d14c875>`; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap index 84ff5a6b6c7..72ffed28cf3 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap @@ -2,4 +2,4 @@ exports[`PrivateCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x29129c06414f4ac73bf889692c7011f91727d4cdbfe4fe143e6adee69b565cc8>`; -exports[`PrivateCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x070e364221be09c89aa9d6cef0a6da965bf61a94b61e1b105728b46da06bdec3>`; +exports[`PrivateCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x0ab17c0893be4023ff61f8c2df5a0106c1709f7b10c2fadd53581da7a7d799d6>`; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap index 6c95202a2fd..4a2e6a33179 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap @@ -6,4 +6,4 @@ exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x15 exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x302550c2014c51737798139c9a80af984fa23be608c9758de295181944dddf66>`; -exports[`PublicCallStackItem computes hash 1`] = `Fr<0x0c1ebcec0faa3cc95bd96dc568e88585d2123d1cdbcb50a4b5f4fc033c58b958>`; +exports[`PublicCallStackItem computes hash 1`] = `Fr<0x1682642d96f9873ed85f245b4ca2ec93d2a0e11ba8e3d614f94ba409030af2c9>`; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap index 6daacfd0635..6cf756de088 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap @@ -2,4 +2,4 @@ exports[`PublicCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x1a2da219bb2e3ac24519fd844365c4f656fc3ba8c58f2960706d25bceb4d1769>`; -exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x0d91b877304f117c8cb1f1b104f4d809fad7360bd2fcdb050c8607cd6a39da81>`; +exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x05db8cb4a08d8d1f5b0f38b2ef50f0bf70b4ed33099f649062326084197f1b79>`; diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 5f8869bb5f5..2398c6564d8 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -36,7 +36,9 @@ export * from './l2_to_l1_message.js'; export * from './max_block_number.js'; export * from './membership_witness.js'; export * from './non_existent_read_request_hints.js'; +export * from './note_hash.js'; export * from './note_hash_read_request_membership_witness.js'; +export * from './nullifier.js'; export * from './nullifier_key_validation_request.js'; export * from './parity/base_parity_inputs.js'; export * from './parity/parity_public_inputs.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/private_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/private_accumulated_data.ts index e626d056ca2..6d074a9e6a8 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_accumulated_data.ts @@ -13,7 +13,9 @@ import { MAX_UNENCRYPTED_LOGS_PER_TX, } from '../../constants.gen.js'; import { CallRequest } from '../call_request.js'; -import { SideEffect, SideEffectLinkedToNoteHash } from '../side_effects.js'; +import { NoteHashContext } from '../note_hash.js'; +import { Nullifier } from '../nullifier.js'; +import { SideEffect } from '../side_effects.js'; /** * Specific accumulated data structure for the final ordering private kernel circuit. It is included @@ -24,11 +26,11 @@ export class PrivateAccumulatedData { /** * The new note hashes made in this transaction. */ - public newNoteHashes: Tuple, + public newNoteHashes: Tuple, /** * The new nullifiers made in this transaction. */ - public newNullifiers: Tuple, + public newNullifiers: Tuple, /** * All the new L2 to L1 messages created in this transaction. */ @@ -88,8 +90,8 @@ export class PrivateAccumulatedData { static fromBuffer(buffer: Buffer | BufferReader): PrivateAccumulatedData { const reader = BufferReader.asReader(buffer); return new PrivateAccumulatedData( - reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, SideEffect), - reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, NoteHashContext), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, Nullifier), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr), reader.readArray(MAX_ENCRYPTED_LOGS_PER_TX, SideEffect), reader.readArray(MAX_UNENCRYPTED_LOGS_PER_TX, SideEffect), @@ -111,8 +113,8 @@ export class PrivateAccumulatedData { static empty() { return new PrivateAccumulatedData( - makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, SideEffect.empty), - makeTuple(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash.empty), + makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, NoteHashContext.empty), + makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Nullifier.empty), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr.zero), makeTuple(MAX_ENCRYPTED_LOGS_PER_TX, SideEffect.empty), makeTuple(MAX_UNENCRYPTED_LOGS_PER_TX, SideEffect.empty), diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts index 9cb9adbd836..5c1a65956ec 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts @@ -2,6 +2,7 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { TxRequest } from '../tx_request.js'; import { PrivateCallData } from './private_call_data.js'; +import { PrivateKernelInnerHints } from './private_kernel_inner_circuit_private_inputs.js'; /** * Input to the private kernel circuit - initial call. @@ -16,6 +17,7 @@ export class PrivateKernelInitCircuitPrivateInputs { * Private calldata corresponding to this iteration of the kernel. */ public privateCall: PrivateCallData, + public hints: PrivateKernelInnerHints, ) {} /** @@ -23,7 +25,7 @@ export class PrivateKernelInitCircuitPrivateInputs { * @returns The buffer. */ toBuffer() { - return serializeToBuffer(this.txRequest, this.privateCall); + return serializeToBuffer(this.txRequest, this.privateCall, this.hints); } /** @@ -33,6 +35,10 @@ export class PrivateKernelInitCircuitPrivateInputs { */ static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelInitCircuitPrivateInputs { const reader = BufferReader.asReader(buffer); - return new PrivateKernelInitCircuitPrivateInputs(reader.readObject(TxRequest), reader.readObject(PrivateCallData)); + return new PrivateKernelInitCircuitPrivateInputs( + reader.readObject(TxRequest), + reader.readObject(PrivateCallData), + reader.readObject(PrivateKernelInnerHints), + ); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_inner_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_inner_circuit_private_inputs.ts index 88496556684..877263c6f4c 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_inner_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_inner_circuit_private_inputs.ts @@ -1,8 +1,22 @@ -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { MAX_NEW_NOTE_HASHES_PER_CALL } from '../../constants.gen.js'; import { PrivateCallData } from './private_call_data.js'; import { PrivateKernelData } from './private_kernel_data.js'; +export class PrivateKernelInnerHints { + constructor(public noteHashNullifierCounters: Tuple) {} + + toBuffer() { + return serializeToBuffer(this.noteHashNullifierCounters); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelInnerHints(reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_CALL)); + } +} + /** * Input to the private kernel circuit - Inner call. */ @@ -16,6 +30,7 @@ export class PrivateKernelInnerCircuitPrivateInputs { * Private calldata corresponding to this iteration of the kernel. */ public privateCall: PrivateCallData, + public hints: PrivateKernelInnerHints, ) {} /** @@ -23,7 +38,7 @@ export class PrivateKernelInnerCircuitPrivateInputs { * @returns The buffer. */ toBuffer() { - return serializeToBuffer(this.previousKernel, this.privateCall); + return serializeToBuffer(this.previousKernel, this.privateCall, this.hints); } /** @@ -36,6 +51,7 @@ export class PrivateKernelInnerCircuitPrivateInputs { return new PrivateKernelInnerCircuitPrivateInputs( reader.readObject(PrivateKernelData), reader.readObject(PrivateCallData), + reader.readObject(PrivateKernelInnerHints), ); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts index 561b3bf0b25..df5d9083cdf 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts @@ -1,4 +1,4 @@ -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { GrumpkinScalar } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { @@ -11,47 +11,70 @@ import { } from '../../constants.gen.js'; import { type GrumpkinPrivateKey } from '../../types/grumpkin_private_key.js'; import { countAccumulatedItems } from '../../utils/index.js'; +import { NoteHashContext } from '../note_hash.js'; +import { Nullifier } from '../nullifier.js'; import { type NullifierReadRequestHints, nullifierReadRequestHintsFromBuffer } from '../read_request_hints.js'; -import { SideEffect, SideEffectLinkedToNoteHash } from '../side_effects.js'; +import { SideEffect } from '../side_effects.js'; import { PrivateKernelData } from './private_kernel_data.js'; -/** - * Input to the private kernel circuit - tail call. - */ -export class PrivateKernelTailCircuitPrivateInputs { +export class PrivateKernelTailOutputs { + constructor( + public noteHashes: Tuple, + public nullifiers: Tuple, + ) {} + + toBuffer() { + return serializeToBuffer(this.noteHashes, this.nullifiers); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelTailOutputs( + reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, NoteHashContext), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, Nullifier), + ); + } +} + +export class PrivateKernelTailHints { constructor( /** - * The previous kernel data + * Contains hints for the transient note hashes to locate corresponding nullifiers. */ - public previousKernel: PrivateKernelData, + public transientNullifierIndexesForNoteHashes: Tuple, /** - * The sorted new note hashes. + * Contains hints for the transient nullifiers to locate corresponding note hashes. */ - public sortedNewNoteHashes: Tuple, + public transientNoteHashIndexesForNullifiers: Tuple, /** - * The sorted new note hashes indexes. Maps original to sorted. + * Contains hints for the transient read requests to localize corresponding commitments. */ - public sortedNewNoteHashesIndexes: Tuple, + public noteHashReadRequestHints: Tuple, /** - * Contains hints for the transient read requests to localize corresponding commitments. + * Contains hints for the nullifier read requests to locate corresponding pending or settled nullifiers. */ - public readCommitmentHints: Tuple, + public nullifierReadRequestHints: NullifierReadRequestHints, + /** - * The sorted new nullifiers. Maps original to sorted. + * The master nullifier secret keys for the nullifier key validation requests. + */ + public masterNullifierSecretKeys: Tuple, + /* + * The sorted new note hashes. */ - public sortedNewNullifiers: Tuple, + public sortedNewNoteHashes: Tuple, /** - * The sorted new nullifiers indexes. + * The sorted new note hashes indexes. Maps original to sorted. */ - public sortedNewNullifiersIndexes: Tuple, + public sortedNewNoteHashesIndexes: Tuple, /** - * Contains hints for the nullifier read requests to locate corresponding pending or settled nullifiers. + * The sorted new nullifiers. Maps original to sorted. */ - public nullifierReadRequestHints: NullifierReadRequestHints, + public sortedNewNullifiers: Tuple, /** - * Contains hints for the transient nullifiers to localize corresponding commitments. + * The sorted new nullifiers indexes. */ - public nullifierCommitmentHints: Tuple, + public sortedNewNullifiersIndexes: Tuple, /** * The sorted encrypted log hashes. */ @@ -68,35 +91,23 @@ export class PrivateKernelTailCircuitPrivateInputs { * The sorted encrypted log hashes indexes. Maps original to sorted. */ public sortedUnencryptedLogHashesIndexes: Tuple, - /** - * The master nullifier secret keys for the nullifier key validation requests. - */ - public masterNullifierSecretKeys: Tuple, ) {} - isForPublic() { - return countAccumulatedItems(this.previousKernel.publicInputs.end.publicCallStack) > 0; - } - - /** - * Serialize this as a buffer. - * @returns The buffer. - */ toBuffer() { return serializeToBuffer( - this.previousKernel, + this.transientNullifierIndexesForNoteHashes, + this.transientNoteHashIndexesForNullifiers, + this.noteHashReadRequestHints, + this.nullifierReadRequestHints, + this.masterNullifierSecretKeys, this.sortedNewNoteHashes, this.sortedNewNoteHashesIndexes, - this.readCommitmentHints, this.sortedNewNullifiers, this.sortedNewNullifiersIndexes, - this.nullifierReadRequestHints, - this.nullifierCommitmentHints, this.sortedEncryptedLogHashes, this.sortedEncryptedLogHashesIndexes, this.sortedUnencryptedLogHashes, this.sortedUnencryptedLogHashesIndexes, - this.masterNullifierSecretKeys, ); } @@ -105,22 +116,62 @@ export class PrivateKernelTailCircuitPrivateInputs { * @param buffer - Buffer or reader to read from. * @returns The deserialized instance. */ - static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelTailCircuitPrivateInputs { + static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); - return new PrivateKernelTailCircuitPrivateInputs( - reader.readObject(PrivateKernelData), - reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, SideEffect), + return new PrivateKernelTailHints( reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), - reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, Fr), - reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash), reader.readNumbers(MAX_NEW_NULLIFIERS_PER_TX), + reader.readNumbers(MAX_NOTE_HASH_READ_REQUESTS_PER_TX), reader.readObject({ fromBuffer: nullifierReadRequestHintsFromBuffer }), - reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, Fr), + reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, NoteHashContext), + reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, Nullifier), + reader.readNumbers(MAX_NEW_NULLIFIERS_PER_TX), reader.readArray(MAX_ENCRYPTED_LOGS_PER_TX, SideEffect), reader.readNumbers(MAX_ENCRYPTED_LOGS_PER_TX), reader.readArray(MAX_UNENCRYPTED_LOGS_PER_TX, SideEffect), reader.readNumbers(MAX_UNENCRYPTED_LOGS_PER_TX), - reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar), + ); + } +} + +/** + * Input to the private kernel circuit - tail call. + */ +export class PrivateKernelTailCircuitPrivateInputs { + constructor( + /** + * The previous kernel data + */ + public previousKernel: PrivateKernelData, + public outputs: PrivateKernelTailOutputs, + public hints: PrivateKernelTailHints, + ) {} + + isForPublic() { + return countAccumulatedItems(this.previousKernel.publicInputs.end.publicCallStack) > 0; + } + + /** + * Serialize this as a buffer. + * @returns The buffer. + */ + toBuffer() { + return serializeToBuffer(this.previousKernel, this.outputs, this.hints); + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelTailCircuitPrivateInputs { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelTailCircuitPrivateInputs( + reader.readObject(PrivateKernelData), + reader.readObject(PrivateKernelTailOutputs), + reader.readObject(PrivateKernelTailHints), ); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts index f064b5ee6ec..9713f143c34 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts @@ -1,6 +1,5 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { MAX_NEW_NULLIFIERS_PER_TX } from '../../constants.gen.js'; import { countAccumulatedItems, mergeAccumulatedData } from '../../utils/index.js'; import { AggregationObject } from '../aggregation_object.js'; import { PartialStateReference } from '../partial_state_reference.js'; @@ -150,22 +149,18 @@ export class PrivateKernelTailCircuitPublicInputs { getNonEmptyNoteHashes() { const noteHashes = this.forPublic - ? mergeAccumulatedData( - MAX_NEW_NULLIFIERS_PER_TX, - this.forPublic.endNonRevertibleData.newNoteHashes, - this.forPublic.end.newNoteHashes, - ).map(n => n.value) + ? mergeAccumulatedData(this.forPublic.endNonRevertibleData.newNoteHashes, this.forPublic.end.newNoteHashes).map( + n => n.value, + ) : this.forRollup!.end.newNoteHashes; return noteHashes.filter(n => !n.isZero()); } getNonEmptyNullifiers() { const nullifiers = this.forPublic - ? mergeAccumulatedData( - MAX_NEW_NULLIFIERS_PER_TX, - this.forPublic.endNonRevertibleData.newNullifiers, - this.forPublic.end.newNullifiers, - ).map(n => n.value) + ? mergeAccumulatedData(this.forPublic.endNonRevertibleData.newNullifiers, this.forPublic.end.newNullifiers).map( + n => n.value, + ) : this.forRollup!.end.newNullifiers; return nullifiers.filter(n => !n.isZero()); } diff --git a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts index 7173ba93f71..3b5ff5917f5 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts @@ -16,19 +16,21 @@ import { } from '../../constants.gen.js'; import { CallRequest } from '../call_request.js'; import { Gas } from '../gas.js'; +import { NoteHash } from '../note_hash.js'; +import { Nullifier } from '../nullifier.js'; import { PublicDataUpdateRequest } from '../public_data_update_request.js'; -import { SideEffect, SideEffectLinkedToNoteHash } from '../side_effects.js'; +import { SideEffect } from '../side_effects.js'; export class PublicAccumulatedData { constructor( /** * The new note hashes made in this transaction. */ - public newNoteHashes: Tuple, + public newNoteHashes: Tuple, /** * The new nullifiers made in this transaction. */ - public newNullifiers: Tuple, + public newNullifiers: Tuple, /** * All the new L2 to L1 messages created in this transaction. */ @@ -122,8 +124,8 @@ export class PublicAccumulatedData { static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); return new this( - reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, SideEffect), - reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, NoteHash), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, Nullifier), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr), reader.readArray(MAX_ENCRYPTED_LOGS_PER_TX, SideEffect), reader.readArray(MAX_UNENCRYPTED_LOGS_PER_TX, SideEffect), @@ -146,8 +148,8 @@ export class PublicAccumulatedData { static empty() { return new this( - makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, SideEffect.empty), - makeTuple(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash.empty), + makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, NoteHash.empty), + makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Nullifier.empty), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr.zero), makeTuple(MAX_ENCRYPTED_LOGS_PER_TX, SideEffect.empty), makeTuple(MAX_UNENCRYPTED_LOGS_PER_TX, SideEffect.empty), diff --git a/yarn-project/circuits.js/src/structs/non_existent_read_request_hints.ts b/yarn-project/circuits.js/src/structs/non_existent_read_request_hints.ts index faea5129fbb..dd4a0004ab0 100644 --- a/yarn-project/circuits.js/src/structs/non_existent_read_request_hints.ts +++ b/yarn-project/circuits.js/src/structs/non_existent_read_request_hints.ts @@ -8,8 +8,12 @@ import { NULLIFIER_TREE_HEIGHT, } from '../constants.gen.js'; import { MembershipWitness } from './membership_witness.js'; +import { Nullifier } from './nullifier.js'; import { NullifierLeafPreimage } from './rollup/nullifier_leaf/index.js'; -import { SideEffectLinkedToNoteHash, type SideEffectType } from './side_effects.js'; + +interface PendingValue { + toBuffer(): Buffer; +} export class NonMembershipHint { constructor(public membershipWitness: MembershipWitness, public leafPreimage: LEAF_PREIMAGE) {} @@ -43,7 +47,7 @@ export class NonExistentReadRequestHints< TREE_HEIGHT extends number, LEAF_PREIMAGE extends IndexedTreeLeafPreimage, PENDING_VALUE_LEN extends number, - PENDING_VALUE extends SideEffectType, + PENDING_VALUE extends PendingValue, > { constructor( /** @@ -63,7 +67,7 @@ export class NonExistentReadRequestHints< TREE_HEIGHT extends number, LEAF_PREIMAGE extends IndexedTreeLeafPreimage, PENDING_VALUE_LEN extends number, - PENDING_VALUE extends SideEffectType, + PENDING_VALUE extends PendingValue, >( buffer: Buffer | BufferReader, readRequestLen: READ_REQUEST_LEN, @@ -98,7 +102,7 @@ export type NullifierNonExistentReadRequestHints = NonExistentReadRequestHints< typeof NULLIFIER_TREE_HEIGHT, IndexedTreeLeafPreimage, typeof MAX_NEW_NULLIFIERS_PER_TX, - SideEffectLinkedToNoteHash + Nullifier >; export function nullifierNonExistentReadRequestHintsFromBuffer( @@ -110,7 +114,7 @@ export function nullifierNonExistentReadRequestHintsFromBuffer( NULLIFIER_TREE_HEIGHT, NullifierLeafPreimage, MAX_NEW_NULLIFIERS_PER_TX, - SideEffectLinkedToNoteHash, + Nullifier, ); } @@ -119,7 +123,7 @@ export class NullifierNonExistentReadRequestHintsBuilder { private readRequestIndex = 0; constructor( - sortedPendingNullifiers: Tuple, + sortedPendingNullifiers: Tuple, sortedPendingNullifierIndexHints: Tuple, ) { this.hints = new NonExistentReadRequestHints( @@ -133,7 +137,7 @@ export class NullifierNonExistentReadRequestHintsBuilder { } static empty() { - const emptySortedPendingNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash.empty); + const emptySortedPendingNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Nullifier.empty); const emptySortedPendingNullifierIndexHints = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, () => 0); return new NullifierNonExistentReadRequestHintsBuilder( emptySortedPendingNullifiers, diff --git a/yarn-project/circuits.js/src/structs/note_hash.ts b/yarn-project/circuits.js/src/structs/note_hash.ts new file mode 100644 index 00000000000..dfe63e8c720 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/note_hash.ts @@ -0,0 +1,72 @@ +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { type Ordered } from '../interfaces/index.js'; + +export class NoteHash { + constructor(public value: Fr, public counter: number) {} + + toFields(): Fr[] { + return [this.value, new Fr(this.counter)]; + } + + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return new NoteHash(reader.readField(), reader.readU32()); + } + + isEmpty() { + return this.value.isZero() && !this.counter; + } + + static empty() { + return new NoteHash(Fr.zero(), 0); + } + + toBuffer(): Buffer { + return serializeToBuffer(this.value, this.counter); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new NoteHash(Fr.fromBuffer(reader), reader.readNumber()); + } + + toString(): string { + return `value=${this.value} counter=${this.counter}`; + } +} + +export class NoteHashContext implements Ordered { + constructor(public value: Fr, public counter: number, public nullifierCounter: number) {} + + toFields(): Fr[] { + return [this.value, new Fr(this.counter), new Fr(this.nullifierCounter)]; + } + + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return new NoteHashContext(reader.readField(), reader.readU32(), reader.readU32()); + } + + isEmpty() { + return this.value.isZero() && !this.counter && !this.nullifierCounter; + } + + static empty() { + return new NoteHashContext(Fr.zero(), 0, 0); + } + + toBuffer(): Buffer { + return serializeToBuffer(this.value, this.counter, this.nullifierCounter); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new NoteHashContext(Fr.fromBuffer(reader), reader.readNumber(), reader.readNumber()); + } + + toString(): string { + return `value=${this.value} counter=${this.counter} nullifierCounter=${this.nullifierCounter}`; + } +} diff --git a/yarn-project/circuits.js/src/structs/nullifier.ts b/yarn-project/circuits.js/src/structs/nullifier.ts new file mode 100644 index 00000000000..176628d5e1d --- /dev/null +++ b/yarn-project/circuits.js/src/structs/nullifier.ts @@ -0,0 +1,38 @@ +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { type Ordered } from '../interfaces/index.js'; + +export class Nullifier implements Ordered { + constructor(public value: Fr, public counter: number, public noteHash: Fr) {} + + toFields(): Fr[] { + return [this.value, new Fr(this.counter), this.noteHash]; + } + + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return new Nullifier(reader.readField(), reader.readU32(), reader.readField()); + } + + isEmpty() { + return this.value.isZero() && !this.counter && this.noteHash.isZero(); + } + + static empty() { + return new Nullifier(Fr.zero(), 0, Fr.zero()); + } + + toBuffer(): Buffer { + return serializeToBuffer(this.value, this.counter, this.noteHash); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new Nullifier(Fr.fromBuffer(reader), reader.readNumber(), Fr.fromBuffer(reader)); + } + + toString(): string { + return `value=${this.value} counter=${this.counter} noteHash=${this.noteHash}`; + } +} diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts index b776f73962a..8b9c3b018e9 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts @@ -26,12 +26,15 @@ import { PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, } from '../constants.gen.js'; import { Header } from '../structs/header.js'; -import { SideEffect, SideEffectLinkedToNoteHash } from '../structs/side_effects.js'; +import { isEmptyArray } from '../utils/index.js'; import { CallContext } from './call_context.js'; import { L2ToL1Message } from './l2_to_l1_message.js'; import { MaxBlockNumber } from './max_block_number.js'; +import { NoteHash } from './note_hash.js'; +import { Nullifier } from './nullifier.js'; import { NullifierKeyValidationRequest } from './nullifier_key_validation_request.js'; import { ReadRequest } from './read_request.js'; +import { SideEffect } from './side_effects.js'; import { TxContext } from './tx_context.js'; /** @@ -78,11 +81,11 @@ export class PrivateCircuitPublicInputs { /** * New note hashes created by the corresponding function call. */ - public newNoteHashes: Tuple, + public newNoteHashes: Tuple, /** * New nullifiers created by the corresponding function call. */ - public newNullifiers: Tuple, + public newNullifiers: Tuple, /** * Private call stack at the current kernel iteration. */ @@ -162,8 +165,8 @@ export class PrivateCircuitPublicInputs { reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, SideEffect), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest), - reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, SideEffect), - reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash), + reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier), reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, Fr), reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, Fr), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), @@ -189,8 +192,8 @@ export class PrivateCircuitPublicInputs { reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, SideEffect), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest), - reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, SideEffect), - reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash), + reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier), reader.readFieldArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL), reader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), @@ -219,8 +222,8 @@ export class PrivateCircuitPublicInputs { makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, SideEffect.empty), makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest.empty), makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest.empty), - makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, SideEffect.empty), - makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash.empty), + makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash.empty), + makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier.empty), makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, Fr.zero), makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, Fr.zero), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message.empty), @@ -236,9 +239,6 @@ export class PrivateCircuitPublicInputs { } isEmpty() { - // eslint-disable-next-line jsdoc/require-jsdoc - const isEmptyArray = (arr: { isEmpty: (...args: any[]) => boolean }[]) => isArrayEmpty(arr, item => item.isEmpty()); - // eslint-disable-next-line jsdoc/require-jsdoc const isZeroArray = (arr: { isZero: (...args: any[]) => boolean }[]) => isArrayEmpty(arr, item => item.isZero()); return ( this.callContext.isEmpty() && diff --git a/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts b/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts index 2fc577a5b8c..df7c6192b6b 100644 --- a/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts @@ -2,7 +2,8 @@ import { randomInt } from '@aztec/foundation/crypto'; import { setupCustomSnapshotSerializers, updateInlineTestData } from '@aztec/foundation/testing'; import { makePublicCallStackItem } from '../tests/factories.js'; -import { AztecAddress, Fr, FunctionData, FunctionSelector, SideEffect } from './index.js'; +import { AztecAddress, Fr, FunctionData, FunctionSelector } from './index.js'; +import { NoteHash } from './note_hash.js'; import { PublicCallStackItem } from './public_call_stack_item.js'; describe('PublicCallStackItem', () => { @@ -33,7 +34,7 @@ describe('PublicCallStackItem', () => { callStack.contractAddress = AztecAddress.fromField(new Fr(1)); callStack.functionData = new FunctionData(new FunctionSelector(2), false); callStack.isExecutionRequest = true; - callStack.publicInputs.newNoteHashes[0] = new SideEffect(new Fr(1), new Fr(0)); + callStack.publicInputs.newNoteHashes[0] = new NoteHash(new Fr(1), 0); const hash = callStack.hash(); expect(hash.toString()).toMatchSnapshot(); @@ -51,7 +52,7 @@ describe('PublicCallStackItem', () => { callStack.contractAddress = AztecAddress.fromField(new Fr(1)); callStack.functionData = new FunctionData(new FunctionSelector(2), false); - callStack.publicInputs.newNoteHashes[0] = new SideEffect(new Fr(1), new Fr(0)); + callStack.publicInputs.newNoteHashes[0] = new NoteHash(new Fr(1), 0); const hash = callStack.hash(); expect(hash.toString()).toMatchSnapshot(); diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts index 90b2f337de6..77b4f05483c 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts @@ -25,6 +25,7 @@ import { MAX_UNENCRYPTED_LOGS_PER_CALL, PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, } from '../constants.gen.js'; +import { isEmptyArray } from '../utils/index.js'; import { CallContext } from './call_context.js'; import { ContractStorageRead } from './contract_storage_read.js'; import { ContractStorageUpdateRequest } from './contract_storage_update_request.js'; @@ -32,9 +33,11 @@ import { Gas } from './gas.js'; import { GlobalVariables } from './global_variables.js'; import { Header } from './header.js'; import { L2ToL1Message } from './l2_to_l1_message.js'; +import { NoteHash } from './note_hash.js'; +import { Nullifier } from './nullifier.js'; import { ReadRequest } from './read_request.js'; import { RevertCode } from './revert_code.js'; -import { SideEffect, SideEffectLinkedToNoteHash } from './side_effects.js'; +import { SideEffect } from './side_effects.js'; /** * Public inputs to a public circuit. @@ -82,11 +85,11 @@ export class PublicCircuitPublicInputs { /** * New note hashes created within a public execution call */ - public newNoteHashes: Tuple, + public newNoteHashes: Tuple, /** * New nullifiers created within a public execution call */ - public newNullifiers: Tuple, + public newNullifiers: Tuple, /** * New L2 to L1 messages generated during the call. */ @@ -158,8 +161,8 @@ export class PublicCircuitPublicInputs { makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, ContractStorageUpdateRequest.empty), makeTuple(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead.empty), makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, Fr.zero), - makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, SideEffect.empty), - makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash.empty), + makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash.empty), + makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier.empty), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message.empty), Fr.ZERO, Fr.ZERO, @@ -176,25 +179,22 @@ export class PublicCircuitPublicInputs { } isEmpty() { - const isSideEffectArrayEmpty = (arr: SideEffect[]) => isArrayEmpty(arr, item => item.isEmpty()); - const isSideEffectLinkedArrayEmpty = (arr: SideEffectLinkedToNoteHash[]) => - isArrayEmpty(arr, item => item.isEmpty()); const isFrArrayEmpty = (arr: Fr[]) => isArrayEmpty(arr, item => item.isZero()); return ( this.callContext.isEmpty() && this.argsHash.isZero() && this.returnsHash.isZero() && - isArrayEmpty(this.nullifierReadRequests, item => item.isEmpty()) && - isArrayEmpty(this.nullifierNonExistentReadRequests, item => item.isEmpty()) && - isArrayEmpty(this.contractStorageUpdateRequests, item => item.isEmpty()) && - isArrayEmpty(this.contractStorageReads, item => item.isEmpty()) && + isEmptyArray(this.nullifierReadRequests) && + isEmptyArray(this.nullifierNonExistentReadRequests) && + isEmptyArray(this.contractStorageUpdateRequests) && + isEmptyArray(this.contractStorageReads) && isFrArrayEmpty(this.publicCallStackHashes) && - isSideEffectArrayEmpty(this.newNoteHashes) && - isSideEffectLinkedArrayEmpty(this.newNullifiers) && - isArrayEmpty(this.newL2ToL1Msgs, item => item.isEmpty()) && + isEmptyArray(this.newNoteHashes) && + isEmptyArray(this.newNullifiers) && + isEmptyArray(this.newL2ToL1Msgs) && this.startSideEffectCounter.isZero() && this.endSideEffectCounter.isZero() && - isArrayEmpty(this.unencryptedLogsHashes, item => item.isEmpty()) && + isEmptyArray(this.unencryptedLogsHashes) && this.unencryptedLogPreimagesLength.isZero() && this.historicalHeader.isEmpty() && this.globalVariables.isEmpty() && @@ -272,8 +272,8 @@ export class PublicCircuitPublicInputs { reader.readArray(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, ContractStorageUpdateRequest), reader.readArray(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead), reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, Fr), - reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, SideEffect), - reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash), + reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), reader.readObject(Fr), reader.readObject(Fr), @@ -301,8 +301,8 @@ export class PublicCircuitPublicInputs { reader.readArray(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, ContractStorageUpdateRequest), reader.readArray(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead), reader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL), - reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, SideEffect), - reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash), + reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash), + reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), reader.readField(), reader.readField(), diff --git a/yarn-project/circuits.js/src/structs/side_effects.ts b/yarn-project/circuits.js/src/structs/side_effects.ts index 9e09b15255a..1bbe54ce470 100644 --- a/yarn-project/circuits.js/src/structs/side_effects.ts +++ b/yarn-project/circuits.js/src/structs/side_effects.ts @@ -92,101 +92,3 @@ export class SideEffect implements SideEffectType { return new SideEffect(Fr.fromBuffer(reader), Fr.fromBuffer(reader)); } } - -/** - * Side-effect object consisting of a value, a start counter and an end counter. - * cpp/src/aztec3/circuits/abis/side_effects.hpp. - */ -export class SideEffectLinkedToNoteHash implements SideEffectType { - constructor( - /** - * The value of the side-effect object. - */ - public value: Fr, - /** - * The note hash corresponding to the side-effect value. - */ - public noteHash: Fr, - /** - * The counter. - */ - public counter: Fr, - ) {} - - toString(): string { - return `value=${this.value.toString()} noteHash=${this.noteHash.toString()} counter=${this.counter.toString()}`; - } - - /** - * Serialize this as a buffer. - * @returns The buffer. - */ - toBuffer(): Buffer { - return serializeToBuffer(this.value, this.noteHash, this.counter); - } - - /** - * Convert to an array of fields. - * @returns The array of fields. - */ - toFields(): Fr[] { - return [this.value, this.noteHash, this.counter]; - } - - static fromFields(fields: Fr[] | FieldReader): SideEffectLinkedToNoteHash { - const reader = FieldReader.asReader(fields); - return new SideEffectLinkedToNoteHash(reader.readField(), reader.readField(), reader.readField()); - } - - /** - * Returns whether this instance of side-effect is empty. - * @returns True if the value, note hash and counter are all zero. - */ - isEmpty() { - return SideEffectLinkedToNoteHash.isEmpty(this); - } - - /** - * Returns whether this instance of side-effect is empty. - * @returns True if the value, note hash and counter are all zero. - */ - static isEmpty(sideEffect: SideEffectLinkedToNoteHash) { - return sideEffect.value.isZero() && sideEffect.noteHash.isZero() && sideEffect.counter.isZero(); - } - - /** - * Returns an empty instance of side-effect. - * @returns Side-effect with value, note hash and counter being zero. - */ - static empty(): SideEffectLinkedToNoteHash { - return new SideEffectLinkedToNoteHash(Fr.zero(), Fr.zero(), Fr.zero()); - } - - /** - * Deserializes from a buffer or reader, corresponding to a write in cpp. - * @param buffer - Buffer or reader to read from. - * @returns A new instance of SideEffectLinkedToNoteHash. - */ - static fromBuffer(buffer: Buffer | BufferReader): SideEffectLinkedToNoteHash { - const reader = BufferReader.asReader(buffer); - return new SideEffectLinkedToNoteHash(Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader)); - } -} - -/** - * Convert an array of side effects to an array only non-empty side effects. - * @param sideEffects - array to be converted - * @returns the array of the non-empty side effects - */ -export function nonEmptySideEffects(sideEffects: SideEffectType[]): SideEffectType[] { - return sideEffects.filter!(sideEffect => !sideEffect.isEmpty()); -} - -/** - * Convert an array of side effects to an array of their values. - * @param sideEffects - array to be converted - * @returns the array of field values (excluding SideEffect metadata like counter) - */ -export function sideEffectArrayToValueArray(sideEffects: SideEffectType[]): Fr[] { - return sideEffects.map(sideEffect => sideEffect.value); -} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 598c4ddace3..6021d2dedd1 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -75,7 +75,10 @@ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_BASE_PARITY_PER_ROOT_PARITY, NUM_MSGS_PER_BASE_PARITY, + NoteHash, + NoteHashContext, NoteHashReadRequestMembershipWitness, + Nullifier, NullifierKeyValidationRequest, NullifierKeyValidationRequestContext, NullifierLeafPreimage, @@ -123,7 +126,6 @@ import { RootRollupInputs, RootRollupPublicInputs, SideEffect, - SideEffectLinkedToNoteHash, StateDiffHints, StateReference, TxContext, @@ -144,8 +146,6 @@ import { GlobalVariables } from '../structs/global_variables.js'; import { Header } from '../structs/header.js'; import { KernelCircuitPublicInputs } from '../structs/kernel/kernel_circuit_public_inputs.js'; import { KernelData } from '../structs/kernel/kernel_data.js'; -import { PrivateKernelInitCircuitPrivateInputs } from '../structs/kernel/private_kernel_init_circuit_private_inputs.js'; -import { PrivateKernelInnerCircuitPrivateInputs } from '../structs/kernel/private_kernel_inner_circuit_private_inputs.js'; import { RollupValidationRequests } from '../structs/rollup_validation_requests.js'; import { ValidationRequests } from '../structs/validation_requests.js'; @@ -158,13 +158,16 @@ export function makeNewSideEffect(seed: number): SideEffect { return new SideEffect(fr(seed), fr(seed + 1)); } -/** - * Creates an arbitrary side effect object (linked to a note hash) with the given seed. - * @param seed - The seed to use for generating the object. - * @returns A side effect object. - */ -export function makeNewSideEffectLinkedToNoteHash(seed: number): SideEffectLinkedToNoteHash { - return new SideEffectLinkedToNoteHash(fr(seed), fr(seed + 1), fr(seed + 2)); +function makeNoteHash(seed: number) { + return new NoteHash(fr(seed), seed + 1); +} + +function makeNoteHashContext(seed: number) { + return new NoteHashContext(fr(seed), seed + 1, seed + 2); +} + +function makeNullifier(seed: number) { + return new Nullifier(fr(seed), seed + 1, fr(seed + 2)); } /** @@ -283,7 +286,7 @@ export function makeContractStorageRead(seed = 1): ContractStorageRead { export function makeValidationRequests(seed = 1) { return new ValidationRequests( makeRollupValidationRequests(seed), - makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, sideEffectFromNumber, seed + 0x80), + makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, makeNewSideEffect, seed + 0x80), makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, makeReadRequestContext, seed + 0x90), makeTuple(MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, makeReadRequestContext, seed + 0x95), makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, makeNullifierKeyValidationRequestContext, seed + 0x100), @@ -338,16 +341,11 @@ export function makePublicAccumulatedData(seed = 1, full = false): PublicAccumul const tupleGenerator = full ? makeTuple : makeHalfFullTuple; return new PublicAccumulatedData( - tupleGenerator(MAX_NEW_NOTE_HASHES_PER_TX, sideEffectFromNumber, seed + 0x120, SideEffect.empty), - tupleGenerator( - MAX_NEW_NULLIFIERS_PER_TX, - sideEffectLinkedFromNumber, - seed + 0x200, - SideEffectLinkedToNoteHash.empty, - ), + tupleGenerator(MAX_NEW_NOTE_HASHES_PER_TX, makeNoteHash, seed + 0x120, NoteHash.empty), + tupleGenerator(MAX_NEW_NULLIFIERS_PER_TX, makeNullifier, seed + 0x200, Nullifier.empty), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x600, Fr.zero), - tupleGenerator(MAX_ENCRYPTED_LOGS_PER_TX, sideEffectFromNumber, seed + 0x700, SideEffect.empty), // encrypted logs hashes - tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_TX, sideEffectFromNumber, seed + 0x800, SideEffect.empty), // unencrypted logs hashes + tupleGenerator(MAX_ENCRYPTED_LOGS_PER_TX, makeNewSideEffect, seed + 0x700, SideEffect.empty), // encrypted logs hashes + tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_TX, makeNewSideEffect, seed + 0x800, SideEffect.empty), // unencrypted logs hashes fr(seed + 0x900), // encrypted_log_preimages_length fr(seed + 0xa00), // unencrypted_log_preimages_length tupleGenerator( @@ -370,16 +368,11 @@ export function makePrivateAccumulatedData(seed = 1, full = false) { const tupleGenerator = full ? makeTuple : makeHalfFullTuple; return new PrivateAccumulatedData( - tupleGenerator(MAX_NEW_NOTE_HASHES_PER_TX, sideEffectFromNumber, seed + 0x120, SideEffect.empty), - tupleGenerator( - MAX_NEW_NULLIFIERS_PER_TX, - sideEffectLinkedFromNumber, - seed + 0x200, - SideEffectLinkedToNoteHash.empty, - ), + tupleGenerator(MAX_NEW_NOTE_HASHES_PER_TX, makeNoteHashContext, seed + 0x120, NoteHashContext.empty), + tupleGenerator(MAX_NEW_NULLIFIERS_PER_TX, makeNullifier, seed + 0x200, Nullifier.empty), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x600, Fr.zero), - tupleGenerator(MAX_ENCRYPTED_LOGS_PER_TX, sideEffectFromNumber, seed + 0x700, SideEffect.empty), // encrypted logs hashes - tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_TX, sideEffectFromNumber, seed + 0x800, SideEffect.empty), // unencrypted logs hashes + tupleGenerator(MAX_ENCRYPTED_LOGS_PER_TX, makeNewSideEffect, seed + 0x700, SideEffect.empty), // encrypted logs hashes + tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_TX, makeNewSideEffect, seed + 0x800, SideEffect.empty), // unencrypted logs hashes fr(seed + 0x900), // encrypted_log_preimages_length fr(seed + 0xa00), // unencrypted_log_preimages_length tupleGenerator(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x400, CallRequest.empty), @@ -446,17 +439,12 @@ export function makePublicCircuitPublicInputs( ), tupleGenerator(MAX_PUBLIC_DATA_READS_PER_CALL, makeContractStorageRead, seed + 0x500, ContractStorageRead.empty), tupleGenerator(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, fr, seed + 0x600, Fr.zero), - tupleGenerator(MAX_NEW_NOTE_HASHES_PER_CALL, makeNewSideEffect, seed + 0x700, SideEffect.empty), - tupleGenerator( - MAX_NEW_NULLIFIERS_PER_CALL, - makeNewSideEffectLinkedToNoteHash, - seed + 0x800, - SideEffectLinkedToNoteHash.empty, - ), + tupleGenerator(MAX_NEW_NOTE_HASHES_PER_CALL, makeNoteHash, seed + 0x700, NoteHash.empty), + tupleGenerator(MAX_NEW_NULLIFIERS_PER_CALL, makeNullifier, seed + 0x800, Nullifier.empty), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, makeL2ToL1Message, seed + 0x900, L2ToL1Message.empty), fr(seed + 0xa00), fr(seed + 0xa01), - tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_CALL, sideEffectFromNumber, seed + 0x901, SideEffect.empty), + tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_CALL, makeNewSideEffect, seed + 0x901, SideEffect.empty), fr(seed + 0x902), makeHeader(seed + 0xa00, undefined), makeGlobalVariables(seed + 0xa01), @@ -706,27 +694,6 @@ export function makeProof(seed = 1) { return makeDynamicSizeBuffer(16, seed); } -/** - * Makes arbitrary private kernel init private inputs - * @param seed - The seed to use for generating the private kernel inputs. - * @returns Private kernel init private inputs. - */ -export function makePrivateKernelInitCircuitPrivateInputs(seed = 1): PrivateKernelInitCircuitPrivateInputs { - return new PrivateKernelInitCircuitPrivateInputs(makeTxRequest(seed), makePrivateCallData(seed + 0x1000)); -} - -/** - * Makes arbitrary private kernel inner private inputs - * @param seed - The seed to use for generating the private kernel inputs. - * @returns Private kernel inner private inputs. - */ -export function makePrivateKernelInnerCircuitPrivateInputs(seed = 1): PrivateKernelInnerCircuitPrivateInputs { - return new PrivateKernelInnerCircuitPrivateInputs( - makePrivateKernelInnerData(seed), - makePrivateCallData(seed + 0x1000), - ); -} - /** * Makes arbitrary call stack item. * @param seed - The seed to use for generating the call stack item. @@ -897,22 +864,22 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn argsHash: fr(seed + 0x100), returnsHash: fr(seed + 0x200), minRevertibleSideEffectCounter: fr(0), - noteHashReadRequests: makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, sideEffectFromNumber, seed + 0x300), + noteHashReadRequests: makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, makeNewSideEffect, seed + 0x300), nullifierReadRequests: makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, makeReadRequest, seed + 0x310), nullifierKeyValidationRequests: makeTuple( MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, makeNullifierKeyValidationRequest, seed + 0x320, ), - newNoteHashes: makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, sideEffectFromNumber, seed + 0x400), - newNullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, sideEffectLinkedFromNumber, seed + 0x500), + newNoteHashes: makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, makeNoteHash, seed + 0x400), + newNullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, makeNullifier, seed + 0x500), privateCallStackHashes: makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, fr, seed + 0x600), publicCallStackHashes: makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, fr, seed + 0x700), newL2ToL1Msgs: makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, makeL2ToL1Message, seed + 0x800), startSideEffectCounter: fr(seed + 0x849), endSideEffectCounter: fr(seed + 0x850), - encryptedLogsHashes: makeTuple(MAX_ENCRYPTED_LOGS_PER_CALL, sideEffectFromNumber, seed + 0x900), - unencryptedLogsHashes: makeTuple(MAX_UNENCRYPTED_LOGS_PER_CALL, sideEffectFromNumber, seed + 0xa00), + encryptedLogsHashes: makeTuple(MAX_ENCRYPTED_LOGS_PER_CALL, makeNewSideEffect, seed + 0x900), + unencryptedLogsHashes: makeTuple(MAX_UNENCRYPTED_LOGS_PER_CALL, makeNewSideEffect, seed + 0xa00), encryptedLogPreimagesLength: fr(seed + 0xb00), unencryptedLogPreimagesLength: fr(seed + 0xc00), historicalHeader: makeHeader(seed + 0xd00, undefined), @@ -1374,21 +1341,3 @@ function makeContractClassPrivateFunction(seed = 0): PrivateFunction { export function fr(n: number): Fr { return new Fr(BigInt(n)); } - -/** - * Test only. Easy to identify big endian side-effect serialize. - * @param n - The number. - * @returns The SideEffect instance. - */ -export function sideEffectFromNumber(n: number): SideEffect { - return new SideEffect(new Fr(BigInt(n)), Fr.zero()); -} - -/** - * Test only. Easy to identify big endian side-effect serialize. - * @param n - The number. - * @returns The SideEffect instance. - */ -export function sideEffectLinkedFromNumber(n: number): SideEffectLinkedToNoteHash { - return new SideEffectLinkedToNoteHash(new Fr(BigInt(n)), Fr.zero(), Fr.zero()); -} diff --git a/yarn-project/circuits.js/src/utils/index.ts b/yarn-project/circuits.js/src/utils/index.ts index 18ce9109990..879d3a0f5a2 100644 --- a/yarn-project/circuits.js/src/utils/index.ts +++ b/yarn-project/circuits.js/src/utils/index.ts @@ -1,3 +1,4 @@ +import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; import { type IsEmpty, type Ordered } from '../interfaces/index.js'; @@ -19,13 +20,13 @@ export function countAccumulatedItems(arr: T[]) { // Merges two arrays of length N into an array of length N. export function mergeAccumulatedData( - _length: N, arr0: Tuple, arr1: Tuple, + length: N = arr0.length as N, // Need this for ts to infer the return Tuple length. ): Tuple { const numNonEmptyItems0 = countAccumulatedItems(arr0); const numNonEmptyItems1 = countAccumulatedItems(arr1); - if (numNonEmptyItems0 + numNonEmptyItems1 > arr0.length) { + if (numNonEmptyItems0 + numNonEmptyItems1 > length) { throw new Error('Combined non-empty items exceeded the maximum allowed.'); } @@ -35,17 +36,48 @@ export function mergeAccumulatedData( } // Sort items by their counters in ascending order. All empty items (counter === 0) are padded to the right. -export function sortByCounter(arr: T[]): T[] { +export function sortByCounter(arr: Tuple): Tuple { return [...arr].sort((a, b) => { if (a.counter === b.counter) { return 0; } - if (a.counter === 0) { + if (a.isEmpty()) { return 1; // Move empty items to the right. } - if (b.counter === 0) { + if (b.isEmpty()) { return -1; // Move non-empty items to the left. } return a.counter - b.counter; + }) as Tuple; +} + +export function sortByCounterGetSortedHints( + arr: Tuple, + length: N = arr.length as N, // Need this for ts to infer the return Tuple length. +): [Tuple, Tuple] { + const itemsWithIndexes = arr.map((item, i) => ({ + item, + originalIndex: i, + counter: item.counter, + isEmpty: () => item.isEmpty(), + })); + const sorted = sortByCounter(itemsWithIndexes); + const items = sorted.map(({ item }) => item) as Tuple; + + const indexHints = makeTuple(length, () => 0); + sorted.forEach(({ originalIndex }, i) => { + if (!items[i].isEmpty()) { + indexHints[originalIndex] = i; + } }); + + return [items, indexHints]; +} + +export function isEmptyArray(arr: T[]): boolean { + return arr.every(item => item.isEmpty()); +} + +export function getNonEmptyItems(arr: T[]): T[] { + return arr.filter(item => !item.isEmpty()); } diff --git a/yarn-project/circuits.js/src/utils/utils.test.ts b/yarn-project/circuits.js/src/utils/utils.test.ts index 3fe9bf564aa..7d604f54bab 100644 --- a/yarn-project/circuits.js/src/utils/utils.test.ts +++ b/yarn-project/circuits.js/src/utils/utils.test.ts @@ -2,7 +2,14 @@ import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; import { type IsEmpty } from '../interfaces/index.js'; -import { countAccumulatedItems, mergeAccumulatedData, sortByCounter } from './index.js'; +import { + countAccumulatedItems, + getNonEmptyItems, + isEmptyArray, + mergeAccumulatedData, + sortByCounter, + sortByCounterGetSortedHints, +} from './index.js'; class TestItem { constructor(public value: number, public counter = 0) {} @@ -16,7 +23,7 @@ class TestItem { } } -describe('hints utils', () => { +describe('utils', () => { const expectEmptyArrays = (arr: IsEmpty[]) => { arr.forEach(item => expect(item.isEmpty()).toBe(true)); }; @@ -51,7 +58,7 @@ describe('hints utils', () => { it('propagates items from arr0', () => { arr0[0] = new TestItem(12); arr0[1] = new TestItem(34); - const res = mergeAccumulatedData(length, arr0, arr1); + const res = mergeAccumulatedData(arr0, arr1); expect(res.slice(0, 2)).toEqual([arr0[0], arr0[1]]); expectEmptyArrays(res.slice(2)); }); @@ -59,7 +66,7 @@ describe('hints utils', () => { it('propagates items from arr1', () => { arr1[0] = new TestItem(1); arr1[1] = new TestItem(2); - const res = mergeAccumulatedData(length, arr0, arr1); + const res = mergeAccumulatedData(arr0, arr1); expect(res.slice(0, 2)).toEqual([arr1[0], arr1[1]]); expectEmptyArrays(res.slice(2)); }); @@ -69,7 +76,7 @@ describe('hints utils', () => { arr0[1] = new TestItem(34); arr1[0] = new TestItem(1); arr1[1] = new TestItem(2); - const res = mergeAccumulatedData(length, arr0, arr1); + const res = mergeAccumulatedData(arr0, arr1); expect(res.slice(0, 4)).toEqual([arr0[0], arr0[1], arr1[0], arr1[1]]); expectEmptyArrays(res.slice(4)); }); @@ -77,7 +84,7 @@ describe('hints utils', () => { it('throws if arr0 contains non-continuous items', () => { arr0[0] = new TestItem(12); arr0[2] = new TestItem(34); - expect(() => mergeAccumulatedData(length, arr0, arr1)).toThrow( + expect(() => mergeAccumulatedData(arr0, arr1)).toThrow( 'Non-empty items must be placed continuously from index 0.', ); }); @@ -85,7 +92,7 @@ describe('hints utils', () => { it('throws if arr1 contains non-continuous items', () => { arr1[0] = new TestItem(12); arr1[2] = new TestItem(34); - expect(() => mergeAccumulatedData(length, arr0, arr1)).toThrow( + expect(() => mergeAccumulatedData(arr0, arr1)).toThrow( 'Non-empty items must be placed continuously from index 0.', ); }); @@ -94,12 +101,10 @@ describe('hints utils', () => { for (let i = 0; i < length; ++i) { arr0[i] = new TestItem(i + 1); } - expect(mergeAccumulatedData(length, arr0, arr1)).toBeDefined(); + expect(mergeAccumulatedData(arr0, arr1)).toBeDefined(); arr1[0] = new TestItem(1234); - expect(() => mergeAccumulatedData(length, arr0, arr1)).toThrow( - 'Combined non-empty items exceeded the maximum allowed.', - ); + expect(() => mergeAccumulatedData(arr0, arr1)).toThrow('Combined non-empty items exceeded the maximum allowed.'); }); }); @@ -201,4 +206,138 @@ describe('hints utils', () => { ]); }); }); + + describe('sortByCounterGetSortedHints', () => { + it('sorts descending items in ascending order', () => { + // Original array is in descending order. + const arr: TestItem[] = []; + for (let i = 0; i < 6; ++i) { + arr[i] = new TestItem(i, 100 - i); + } + + const [sorted, hints] = sortByCounterGetSortedHints(arr); + + for (let i = 1; i < arr.length; ++i) { + expect(sorted[i].counter).toBeGreaterThan(sorted[i - 1].counter); + expect(hints[i]).toBe(arr.length - i - 1); // Index is reversed. + } + expect(sorted).toEqual(arr.slice().reverse()); + }); + + it('sorts ascending items in ascending order', () => { + const arr: TestItem[] = []; + for (let i = 0; i < 6; ++i) { + arr[i] = new TestItem(i, i + 1); + } + + const [sorted, hints] = sortByCounterGetSortedHints(arr); + + for (let i = 1; i < arr.length; ++i) { + expect(sorted[i].counter).toBeGreaterThan(sorted[i - 1].counter); + expect(hints[i]).toBe(i); // Index is preserved. + } + expect(sorted).toEqual(arr); + }); + + it('sorts random items in ascending order', () => { + const arr: TestItem[] = [ + new TestItem(2, 13), + new TestItem(3, 328), + new TestItem(4, 4), + new TestItem(5, 59), + new TestItem(6, 1), + ]; + + const [sorted, hints] = sortByCounterGetSortedHints(arr); + + expect(sorted).toEqual([ + new TestItem(6, 1), + new TestItem(4, 4), + new TestItem(2, 13), + new TestItem(5, 59), + new TestItem(3, 328), + ]); + + expect(hints).toEqual([2, 4, 1, 3, 0]); + }); + + it('sorts random items and keep empty items to the right', () => { + const arr: TestItem[] = [ + new TestItem(2, 13), + new TestItem(3, 328), + new TestItem(4, 4), + new TestItem(5, 59), + new TestItem(6, 27), + TestItem.empty(), + TestItem.empty(), + ]; + + const [sorted, hints] = sortByCounterGetSortedHints(arr); + + expect(sorted).toEqual([ + new TestItem(4, 4), + new TestItem(2, 13), + new TestItem(6, 27), + new TestItem(5, 59), + new TestItem(3, 328), + TestItem.empty(), + TestItem.empty(), + ]); + + expect(hints).toEqual([1, 4, 0, 3, 2, 0, 0]); + }); + + it('does not mix 0 counter with empty items', () => { + const arr: TestItem[] = [ + new TestItem(3, 328), + new TestItem(2, 0), + new TestItem(6, 27), + TestItem.empty(), + TestItem.empty(), + ]; + + const [sorted, hints] = sortByCounterGetSortedHints(arr); + + expect(sorted).toEqual([ + new TestItem(2, 0), + new TestItem(6, 27), + new TestItem(3, 328), + TestItem.empty(), + TestItem.empty(), + ]); + + expect(hints).toEqual([2, 0, 1, 0, 0]); + }); + }); + + describe('isEmptyArray', () => { + it('returns true if all items in an array are empty', () => { + const arr = [TestItem.empty(), TestItem.empty(), TestItem.empty()]; + expect(isEmptyArray(arr)).toBe(true); + }); + + it('returns false if at least one item in an array is not empty', () => { + { + const arr = [new TestItem(0, 1), TestItem.empty(), TestItem.empty()]; + expect(isEmptyArray(arr)).toBe(false); + } + + { + const arr = [TestItem.empty(), TestItem.empty(), new TestItem(1, 0)]; + expect(isEmptyArray(arr)).toBe(false); + } + }); + }); + + describe('getNonEmptyItems', () => { + it('returns non empty items in an array', () => { + const arr = [new TestItem(0, 1), TestItem.empty(), new TestItem(2, 0), TestItem.empty()]; + expect(getNonEmptyItems(arr)).toEqual([new TestItem(0, 1), new TestItem(2, 0)]); + }); + + it('returns empty array if all items are empty', () => { + const arr = [TestItem.empty(), TestItem.empty(), TestItem.empty()]; + expect(getNonEmptyItems(arr)).toEqual([]); + }); + }); }); diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 64137c301ef..ffb03195a46 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -51,7 +51,10 @@ import { type NULLIFIER_TREE_HEIGHT, NUM_BYTES_PER_SHA256, type NonMembershipHint, + NoteHash, + NoteHashContext, type NoteHashReadRequestMembershipWitness, + Nullifier, NullifierKeyValidationRequest, NullifierKeyValidationRequestContext, type NullifierLeafPreimage, @@ -73,8 +76,11 @@ import { type PrivateKernelData, type PrivateKernelInitCircuitPrivateInputs, type PrivateKernelInnerCircuitPrivateInputs, + type PrivateKernelInnerHints, type PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailCircuitPublicInputs, + type PrivateKernelTailHints, + type PrivateKernelTailOutputs, PublicAccumulatedData, type PublicCallData, type PublicCallStackItem, @@ -102,7 +108,6 @@ import { RootRollupPublicInputs, type SettledReadHint, SideEffect, - SideEffectLinkedToNoteHash, type StateDiffHints, StateReference, TxContext, @@ -134,9 +139,12 @@ import { type EthAddress as NoirEthAddress, type Field as NoirField, type GrumpkinPoint as NoirPoint, + type NoteHashContext as NoteHashContextNoir, + type NoteHash as NoteHashNoir, type NoteHashReadRequestMembershipWitness as NoteHashReadRequestMembershipWitnessNoir, type NullifierKeyValidationRequestContext as NullifierKeyValidationRequestContextNoir, type NullifierKeyValidationRequest as NullifierKeyValidationRequestNoir, + type Nullifier as NullifierNoir, type PrivateAccumulatedData as PrivateAccumulatedDataNoir, type PrivateCallData as PrivateCallDataNoir, type PrivateCallStackItem as PrivateCallStackItemNoir, @@ -147,13 +155,15 @@ import { type ReadRequestContext as ReadRequestContextNoir, type ReadRequest as ReadRequestNoir, type RollupValidationRequests as RollupValidationRequestsNoir, - type SideEffectLinkedToNoteHash as SideEffectLinkedToNoteHashNoir, type SideEffect as SideEffectNoir, type TxContext as TxContextNoir, type TxRequest as TxRequestNoir, type ValidationRequests as ValidationRequestsNoir, } from './types/private_kernel_init_types.js'; -import { type PrivateKernelInnerCircuitPrivateInputs as PrivateKernelInnerCircuitPrivateInputsNoir } from './types/private_kernel_inner_types.js'; +import { + type PrivateKernelInnerCircuitPrivateInputs as PrivateKernelInnerCircuitPrivateInputsNoir, + type PrivateKernelInnerHints as PrivateKernelInnerHintsNoir, +} from './types/private_kernel_inner_types.js'; import { type PrivateKernelTailToPublicCircuitPrivateInputs as PrivateKernelTailToPublicCircuitPrivateInputsNoir } from './types/private_kernel_tail_to_public_types.js'; import { type CombinedAccumulatedData as CombinedAccumulatedDataNoir, @@ -164,6 +174,8 @@ import { type PendingReadHint as PendingReadHintNoir, type PrivateKernelData as PrivateKernelDataNoir, type PrivateKernelTailCircuitPrivateInputs as PrivateKernelTailCircuitPrivateInputsNoir, + type PrivateKernelTailHints as PrivateKernelTailHintsNoir, + type PrivateKernelTailOutputs as PrivateKernelTailOutputsNoir, type ReadRequestStatus as ReadRequestStatusNoir, } from './types/private_kernel_tail_types.js'; import { @@ -528,6 +540,49 @@ export function mapCallRequestToNoir(callRequest: CallRequest): CallRequestNoir }; } +function mapNoteHashToNoir(noteHash: NoteHash): NoteHashNoir { + return { + value: mapFieldToNoir(noteHash.value), + counter: mapNumberToNoir(noteHash.counter), + }; +} + +function mapNoteHashFromNoir(noteHash: NoteHashNoir) { + return new NoteHash(mapFieldFromNoir(noteHash.value), mapNumberFromNoir(noteHash.counter)); +} + +function mapNoteHashContextToNoir(noteHash: NoteHashContext): NoteHashContextNoir { + return { + value: mapFieldToNoir(noteHash.value), + counter: mapNumberToNoir(noteHash.counter), + nullifier_counter: mapNumberToNoir(noteHash.nullifierCounter), + }; +} + +function mapNoteHashContextFromNoir(noteHash: NoteHashContextNoir) { + return new NoteHashContext( + mapFieldFromNoir(noteHash.value), + mapNumberFromNoir(noteHash.counter), + mapNumberFromNoir(noteHash.nullifier_counter), + ); +} + +function mapNullifierToNoir(nullifier: Nullifier): NullifierNoir { + return { + value: mapFieldToNoir(nullifier.value), + counter: mapNumberToNoir(nullifier.counter), + note_hash: mapFieldToNoir(nullifier.noteHash), + }; +} + +function mapNullifierFromNoir(nullifier: NullifierNoir) { + return new Nullifier( + mapFieldFromNoir(nullifier.value), + mapNumberFromNoir(nullifier.counter), + mapFieldFromNoir(nullifier.note_hash), + ); +} + /** * Maps a SideEffect to a noir side effect. * @param sideEffect - The SideEffect. @@ -549,36 +604,6 @@ export function mapSideEffectFromNoir(sideEffect: SideEffectNoir): SideEffect { return new SideEffect(mapFieldFromNoir(sideEffect.value), mapFieldFromNoir(sideEffect.counter)); } -/** - * Maps a SideEffectLinked to a noir side effect. - * @param sideEffectLinked - The side effect linked to note hash. - * @returns The noir SideEffectLinkedToNoteHash. - */ -export function mapSideEffectLinkedToNoir( - sideEffectLinked: SideEffectLinkedToNoteHash, -): SideEffectLinkedToNoteHashNoir { - return { - value: mapFieldToNoir(sideEffectLinked.value), - note_hash: mapFieldToNoir(sideEffectLinked.noteHash), - counter: mapFieldToNoir(sideEffectLinked.counter), - }; -} - -/** - * Maps a noir side effect to aSideEffect. - * @param sideEffect - The noir side effect. - * @returns The TS side effect. - */ -export function mapSideEffectLinkedFromNoir( - sideEffectLinked: SideEffectLinkedToNoteHashNoir, -): SideEffectLinkedToNoteHash { - return new SideEffectLinkedToNoteHash( - mapFieldFromNoir(sideEffectLinked.value), - mapFieldFromNoir(sideEffectLinked.note_hash), - mapFieldFromNoir(sideEffectLinked.counter), - ); -} - /** * Maps a ReadRequest to a noir ReadRequest. * @param readRequest - The read request. @@ -715,8 +740,8 @@ export function mapPrivateCircuitPublicInputsToNoir( privateCircuitPublicInputs.nullifierKeyValidationRequests, mapNullifierKeyValidationRequestToNoir, ), - new_note_hashes: mapTuple(privateCircuitPublicInputs.newNoteHashes, mapSideEffectToNoir), - new_nullifiers: mapTuple(privateCircuitPublicInputs.newNullifiers, mapSideEffectLinkedToNoir), + new_note_hashes: mapTuple(privateCircuitPublicInputs.newNoteHashes, mapNoteHashToNoir), + new_nullifiers: mapTuple(privateCircuitPublicInputs.newNullifiers, mapNullifierToNoir), private_call_stack_hashes: mapTuple(privateCircuitPublicInputs.privateCallStackHashes, mapFieldToNoir), public_call_stack_hashes: mapTuple(privateCircuitPublicInputs.publicCallStackHashes, mapFieldToNoir), new_l2_to_l1_msgs: mapTuple(privateCircuitPublicInputs.newL2ToL1Msgs, mapL2ToL1MessageToNoir), @@ -948,7 +973,7 @@ function mapNullifierNonExistentReadRequestHintsToNoir( ): NullifierNonExistentReadRequestHintsNoir { return { non_membership_hints: mapTuple(hints.nonMembershipHints, mapNullifierNonMembershipHintToNoir), - sorted_pending_values: mapTuple(hints.sortedPendingValues, mapSideEffectLinkedToNoir), + sorted_pending_values: mapTuple(hints.sortedPendingValues, mapNullifierToNoir), sorted_pending_value_index_hints: mapTuple(hints.sortedPendingValueHints, mapNumberToNoir), next_pending_value_indices: mapTuple(hints.nextPendingValueIndices, mapNumberToNoir), }; @@ -1016,8 +1041,8 @@ export function mapPrivateAccumulatedDataFromNoir( privateAccumulatedData: PrivateAccumulatedDataNoir, ): PrivateAccumulatedData { return new PrivateAccumulatedData( - mapTupleFromNoir(privateAccumulatedData.new_note_hashes, MAX_NEW_NOTE_HASHES_PER_TX, mapSideEffectFromNoir), - mapTupleFromNoir(privateAccumulatedData.new_nullifiers, MAX_NEW_NULLIFIERS_PER_TX, mapSideEffectLinkedFromNoir), + mapTupleFromNoir(privateAccumulatedData.new_note_hashes, MAX_NEW_NOTE_HASHES_PER_TX, mapNoteHashContextFromNoir), + mapTupleFromNoir(privateAccumulatedData.new_nullifiers, MAX_NEW_NULLIFIERS_PER_TX, mapNullifierFromNoir), mapTupleFromNoir(privateAccumulatedData.new_l2_to_l1_msgs, MAX_NEW_L2_TO_L1_MSGS_PER_TX, mapFieldFromNoir), mapTupleFromNoir(privateAccumulatedData.encrypted_logs_hashes, MAX_ENCRYPTED_LOGS_PER_TX, mapSideEffectFromNoir), mapTupleFromNoir( @@ -1042,8 +1067,8 @@ export function mapPrivateAccumulatedDataFromNoir( export function mapPrivateAccumulatedDataToNoir(data: PrivateAccumulatedData): PrivateAccumulatedDataNoir { return { - new_note_hashes: mapTuple(data.newNoteHashes, mapSideEffectToNoir), - new_nullifiers: mapTuple(data.newNullifiers, mapSideEffectLinkedToNoir), + new_note_hashes: mapTuple(data.newNoteHashes, mapNoteHashContextToNoir), + new_nullifiers: mapTuple(data.newNullifiers, mapNullifierToNoir), new_l2_to_l1_msgs: mapTuple(data.newL2ToL1Msgs, mapFieldToNoir), encrypted_logs_hashes: mapTuple(data.encryptedLogsHashes, mapSideEffectToNoir), unencrypted_logs_hashes: mapTuple(data.unencryptedLogsHashes, mapSideEffectToNoir), @@ -1058,8 +1083,8 @@ export function mapPublicAccumulatedDataFromNoir( publicAccumulatedData: PublicAccumulatedDataNoir, ): PublicAccumulatedData { return new PublicAccumulatedData( - mapTupleFromNoir(publicAccumulatedData.new_note_hashes, MAX_NEW_NOTE_HASHES_PER_TX, mapSideEffectFromNoir), - mapTupleFromNoir(publicAccumulatedData.new_nullifiers, MAX_NEW_NULLIFIERS_PER_TX, mapSideEffectLinkedFromNoir), + mapTupleFromNoir(publicAccumulatedData.new_note_hashes, MAX_NEW_NOTE_HASHES_PER_TX, mapNoteHashFromNoir), + mapTupleFromNoir(publicAccumulatedData.new_nullifiers, MAX_NEW_NULLIFIERS_PER_TX, mapNullifierFromNoir), mapTupleFromNoir(publicAccumulatedData.new_l2_to_l1_msgs, MAX_NEW_L2_TO_L1_MSGS_PER_TX, mapFieldFromNoir), mapTupleFromNoir(publicAccumulatedData.encrypted_logs_hashes, MAX_ENCRYPTED_LOGS_PER_TX, mapSideEffectFromNoir), mapTupleFromNoir(publicAccumulatedData.unencrypted_logs_hashes, MAX_UNENCRYPTED_LOGS_PER_TX, mapSideEffectFromNoir), @@ -1083,8 +1108,8 @@ export function mapPublicAccumulatedDataToNoir( publicAccumulatedData: PublicAccumulatedData, ): PublicAccumulatedDataNoir { return { - new_note_hashes: mapTuple(publicAccumulatedData.newNoteHashes, mapSideEffectToNoir), - new_nullifiers: mapTuple(publicAccumulatedData.newNullifiers, mapSideEffectLinkedToNoir), + new_note_hashes: mapTuple(publicAccumulatedData.newNoteHashes, mapNoteHashToNoir), + new_nullifiers: mapTuple(publicAccumulatedData.newNullifiers, mapNullifierToNoir), new_l2_to_l1_msgs: mapTuple(publicAccumulatedData.newL2ToL1Msgs, mapFieldToNoir), encrypted_logs_hashes: mapTuple(publicAccumulatedData.encryptedLogsHashes, mapSideEffectToNoir), unencrypted_logs_hashes: mapTuple(publicAccumulatedData.unencryptedLogsHashes, mapSideEffectToNoir), @@ -1348,41 +1373,67 @@ export function mapPrivateKernelTailCircuitPublicInputsForPublicFromNoir( ); } +function mapPrivateKernelInnerHintsToNoir(inputs: PrivateKernelInnerHints): PrivateKernelInnerHintsNoir { + return { + note_hash_nullifier_counters: mapTuple(inputs.noteHashNullifierCounters, mapNumberToNoir), + }; +} + export function mapPrivateKernelInitCircuitPrivateInputsToNoir( - privateKernelInputsInit: PrivateKernelInitCircuitPrivateInputs, + inputs: PrivateKernelInitCircuitPrivateInputs, ): PrivateKernelInitCircuitPrivateInputsNoir { return { - tx_request: mapTxRequestToNoir(privateKernelInputsInit.txRequest), - private_call: mapPrivateCallDataToNoir(privateKernelInputsInit.privateCall), + tx_request: mapTxRequestToNoir(inputs.txRequest), + private_call: mapPrivateCallDataToNoir(inputs.privateCall), + hints: mapPrivateKernelInnerHintsToNoir(inputs.hints), }; } export function mapPrivateKernelInnerCircuitPrivateInputsToNoir( - privateKernelInnerCircuitPrivateInputs: PrivateKernelInnerCircuitPrivateInputs, + inputs: PrivateKernelInnerCircuitPrivateInputs, ): PrivateKernelInnerCircuitPrivateInputsNoir { return { - previous_kernel: mapPrivateKernelDataToNoir(privateKernelInnerCircuitPrivateInputs.previousKernel), - private_call: mapPrivateCallDataToNoir(privateKernelInnerCircuitPrivateInputs.privateCall), + previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), + private_call: mapPrivateCallDataToNoir(inputs.privateCall), + hints: mapPrivateKernelInnerHintsToNoir(inputs.hints), }; } -export function mapPrivateKernelTailCircuitPrivateInputsToNoir( - inputs: PrivateKernelTailCircuitPrivateInputs, -): PrivateKernelTailCircuitPrivateInputsNoir { +function mapPrivateKernelTailOutputsToNoir(inputs: PrivateKernelTailOutputs): PrivateKernelTailOutputsNoir { return { - previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapSideEffectToNoir), + note_hashes: mapTuple(inputs.noteHashes, mapNoteHashContextToNoir), + nullifiers: mapTuple(inputs.nullifiers, mapNullifierToNoir), + }; +} + +function mapPrivateKernelTailHintsToNoir(inputs: PrivateKernelTailHints): PrivateKernelTailHintsNoir { + return { + transient_nullifier_indexes_for_note_hashes: mapTuple( + inputs.transientNullifierIndexesForNoteHashes, + mapNumberToNoir, + ), + transient_note_hash_indexes_for_nullifiers: mapTuple(inputs.transientNoteHashIndexesForNullifiers, mapNumberToNoir), + note_hash_read_request_hints: mapTuple(inputs.noteHashReadRequestHints, mapFieldToNoir), + nullifier_read_request_hints: mapNullifierReadRequestHintsToNoir(inputs.nullifierReadRequestHints), + master_nullifier_secret_keys: mapTuple(inputs.masterNullifierSecretKeys, mapGrumpkinPrivateKeyToNoir), + sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapNoteHashContextToNoir), sorted_new_note_hashes_indexes: mapTuple(inputs.sortedNewNoteHashesIndexes, mapNumberToNoir), - read_commitment_hints: mapTuple(inputs.readCommitmentHints, mapFieldToNoir), - sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapSideEffectLinkedToNoir), + sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapNullifierToNoir), sorted_new_nullifiers_indexes: mapTuple(inputs.sortedNewNullifiersIndexes, mapNumberToNoir), - nullifier_read_request_hints: mapNullifierReadRequestHintsToNoir(inputs.nullifierReadRequestHints), - nullifier_commitment_hints: mapTuple(inputs.nullifierCommitmentHints, mapFieldToNoir), sorted_encrypted_log_hashes: mapTuple(inputs.sortedEncryptedLogHashes, mapSideEffectToNoir), sorted_encrypted_log_hashes_indexes: mapTuple(inputs.sortedEncryptedLogHashesIndexes, mapNumberToNoir), sorted_unencrypted_log_hashes: mapTuple(inputs.sortedUnencryptedLogHashes, mapSideEffectToNoir), sorted_unencrypted_log_hashes_indexes: mapTuple(inputs.sortedUnencryptedLogHashesIndexes, mapNumberToNoir), - master_nullifier_secret_keys: mapTuple(inputs.masterNullifierSecretKeys, mapGrumpkinPrivateKeyToNoir), + }; +} + +export function mapPrivateKernelTailCircuitPrivateInputsToNoir( + inputs: PrivateKernelTailCircuitPrivateInputs, +): PrivateKernelTailCircuitPrivateInputsNoir { + return { + previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), + outputs: mapPrivateKernelTailOutputsToNoir(inputs.outputs), + hints: mapPrivateKernelTailHintsToNoir(inputs.hints), }; } @@ -1391,18 +1442,8 @@ export function mapPrivateKernelTailToPublicCircuitPrivateInputsToNoir( ): PrivateKernelTailToPublicCircuitPrivateInputsNoir { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapSideEffectToNoir), - sorted_new_note_hashes_indexes: mapTuple(inputs.sortedNewNoteHashesIndexes, mapNumberToNoir), - read_commitment_hints: mapTuple(inputs.readCommitmentHints, mapFieldToNoir), - sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapSideEffectLinkedToNoir), - sorted_new_nullifiers_indexes: mapTuple(inputs.sortedNewNullifiersIndexes, mapNumberToNoir), - nullifier_read_request_hints: mapNullifierReadRequestHintsToNoir(inputs.nullifierReadRequestHints), - nullifier_commitment_hints: mapTuple(inputs.nullifierCommitmentHints, mapFieldToNoir), - sorted_encrypted_log_hashes: mapTuple(inputs.sortedEncryptedLogHashes, mapSideEffectToNoir), - sorted_encrypted_log_hashes_indexes: mapTuple(inputs.sortedEncryptedLogHashesIndexes, mapNumberToNoir), - sorted_unencrypted_log_hashes: mapTuple(inputs.sortedUnencryptedLogHashes, mapSideEffectToNoir), - sorted_unencrypted_log_hashes_indexes: mapTuple(inputs.sortedUnencryptedLogHashesIndexes, mapNumberToNoir), - master_nullifier_secret_keys: mapTuple(inputs.masterNullifierSecretKeys, mapGrumpkinPrivateKeyToNoir), + outputs: mapPrivateKernelTailOutputsToNoir(inputs.outputs), + hints: mapPrivateKernelTailHintsToNoir(inputs.hints), }; } @@ -1548,8 +1589,8 @@ export function mapPublicCircuitPublicInputsToNoir( ), contract_storage_reads: mapTuple(publicInputs.contractStorageReads, mapStorageReadToNoir), public_call_stack_hashes: mapTuple(publicInputs.publicCallStackHashes, mapFieldToNoir), - new_note_hashes: mapTuple(publicInputs.newNoteHashes, mapSideEffectToNoir), - new_nullifiers: mapTuple(publicInputs.newNullifiers, mapSideEffectLinkedToNoir), + new_note_hashes: mapTuple(publicInputs.newNoteHashes, mapNoteHashToNoir), + new_nullifiers: mapTuple(publicInputs.newNullifiers, mapNullifierToNoir), new_l2_to_l1_msgs: mapTuple(publicInputs.newL2ToL1Msgs, mapL2ToL1MessageToNoir), start_side_effect_counter: mapFieldToNoir(publicInputs.startSideEffectCounter), end_side_effect_counter: mapFieldToNoir(publicInputs.endSideEffectCounter), diff --git a/yarn-project/pxe/src/kernel_prover/hints_builder.ts b/yarn-project/pxe/src/kernel_prover/hints_builder.ts deleted file mode 100644 index 57e3155f042..00000000000 --- a/yarn-project/pxe/src/kernel_prover/hints_builder.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { - Fr, - GrumpkinScalar, - type MAX_NEW_NOTE_HASHES_PER_TX, - MAX_NEW_NULLIFIERS_PER_TX, - MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, - type MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MembershipWitness, - NULLIFIER_TREE_HEIGHT, - type NullifierKeyValidationRequestContext, - type ReadRequestContext, - type SideEffect, - type SideEffectLinkedToNoteHash, - type SideEffectType, - buildNullifierReadRequestHints, -} from '@aztec/circuits.js'; -import { makeTuple } from '@aztec/foundation/array'; -import { type Tuple } from '@aztec/foundation/serialize'; - -import { type ProvingDataOracle } from './proving_data_oracle.js'; - -export class HintsBuilder { - constructor(private oracle: ProvingDataOracle) {} - - sortSideEffects( - sideEffects: Tuple, - ): [Tuple, Tuple] { - const sorted = sideEffects - .map((sideEffect, index) => ({ sideEffect, index })) - .sort((a, b) => { - // Empty ones go to the right - if (a.sideEffect.isEmpty()) { - return 1; - } - return Number(a.sideEffect.counter.toBigInt() - b.sideEffect.counter.toBigInt()); - }); - - const originalToSorted = sorted.map(() => 0); - sorted.forEach(({ index }, i) => { - originalToSorted[index] = i; - }); - - return [sorted.map(({ sideEffect }) => sideEffect) as Tuple, originalToSorted as Tuple]; - } - - /** - * Performs the matching between an array of read request and an array of note hashes. This produces - * hints for the private kernel tail circuit to efficiently match a read request with the corresponding - * note hash. Several read requests might be pointing to the same note hash. It is therefore valid - * to return more than one hint with the same index (contrary to getNullifierHints). - * - * @param noteHashReadRequests - The array of read requests. - * @param noteHashes - The array of note hashes. - * @returns An array of hints where each element is the index of the note hash in note hashes array - * corresponding to the read request. In other words we have readRequests[i] == noteHashes[hints[i]]. - */ - getNoteHashReadRequestHints( - noteHashReadRequests: Tuple, - noteHashes: Tuple, - ): Tuple { - const hints = makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, Fr.zero); - for (let i = 0; i < MAX_NOTE_HASH_READ_REQUESTS_PER_TX && !noteHashReadRequests[i].isEmpty(); i++) { - const equalToRR = (cmt: SideEffect) => cmt.value.equals(noteHashReadRequests[i].value); - const result = noteHashes.findIndex(equalToRR); - if (result == -1) { - throw new Error( - `The read request at index ${i} ${noteHashReadRequests[i].toString()} does not match to any note hash.`, - ); - } else { - hints[i] = new Fr(result); - } - } - return hints; - } - - getNullifierReadRequestHints( - nullifierReadRequests: Tuple, - nullifiers: Tuple, - ) { - return buildNullifierReadRequestHints(this, nullifierReadRequests, nullifiers); - } - - async getNullifierMembershipWitness(nullifier: Fr) { - const res = await this.oracle.getNullifierMembershipWitness(nullifier); - if (!res) { - throw new Error(`Cannot find the leaf for nullifier ${nullifier.toBigInt()}.`); - } - - const { index, siblingPath, leafPreimage } = res; - return { - membershipWitness: new MembershipWitness( - NULLIFIER_TREE_HEIGHT, - index, - siblingPath.toTuple(), - ), - leafPreimage, - }; - } - - /** - * Performs the matching between an array of nullified note hashes and an array of note hashes. This produces - * hints for the private kernel tail circuit to efficiently match a nullifier with the corresponding - * note hash. Note that the same note hash value might appear more than once in the note hashes - * (resp. nullified note hashes) array. It is crucial in this case that each hint points to a different index - * of the nullified note hashes array. Otherwise, the private kernel will fail to validate. - * - * @param nullifiedNoteHashes - The array of nullified note hashes. - * @param noteHashes - The array of note hashes. - * @returns An array of hints where each element is the index of the note hash in note hashes array - * corresponding to the nullified note hash. In other words we have nullifiedNoteHashes[i] == noteHashes[hints[i]]. - */ - getNullifierHints( - nullifiedNoteHashes: Tuple, - noteHashes: Tuple, - ): Tuple { - const hints = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Fr.zero); - const alreadyUsed = new Set(); - for (let i = 0; i < MAX_NEW_NULLIFIERS_PER_TX; i++) { - if (!nullifiedNoteHashes[i].isZero()) { - const result = noteHashes.findIndex( - (cmt: SideEffect, index: number) => cmt.value.equals(nullifiedNoteHashes[i]) && !alreadyUsed.has(index), - ); - alreadyUsed.add(result); - if (result == -1) { - throw new Error( - `The nullified note hash at index ${i} with value ${nullifiedNoteHashes[ - i - ].toString()} does not match to any note hash.`, - ); - } else { - hints[i] = new Fr(result); - } - } - } - return hints; - } - - async getMasterNullifierSecretKeys( - nullifierKeyValidationRequests: Tuple< - NullifierKeyValidationRequestContext, - typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX - >, - ) { - const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero); - for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) { - const request = nullifierKeyValidationRequests[i]; - if (request.isEmpty()) { - break; - } - keys[i] = await this.oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey); - } - return keys; - } -} 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 c8638d6dc95..0179add5ec3 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -6,12 +6,13 @@ import { MAX_NEW_NOTE_HASHES_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MembershipWitness, + NoteHash, + NoteHashContext, NoteHashReadRequestMembershipWitness, PrivateCallStackItem, PrivateCircuitPublicInputs, PrivateKernelCircuitPublicInputs, PrivateKernelTailCircuitPublicInputs, - SideEffect, type TxRequest, VK_TREE_HEIGHT, VerificationKey, @@ -55,8 +56,8 @@ describe('Kernel Prover', () => { MAX_NEW_NOTE_HASHES_PER_CALL, i => i < newNoteIndices.length - ? new SideEffect(generateFakeCommitment(notesAndSlots[newNoteIndices[i]]), Fr.ZERO) - : SideEffect.empty(), + ? new NoteHash(generateFakeCommitment(notesAndSlots[newNoteIndices[i]]), 0) + : NoteHash.empty(), 0, ); const functionData = FunctionData.empty(); @@ -66,6 +67,7 @@ describe('Kernel Prover', () => { nestedExecutions: (dependencies[fnName] || []).map(name => createExecutionResult(name)), vk: VerificationKey.makeFake().toBuffer(), newNotes: newNoteIndices.map(idx => notesAndSlots[idx]), + nullifiedNoteHashCounters: [], // TODO(dbanks12): should test kernel prover with non-transient reads. // This will be necessary once kernel actually checks (attempts to match) transient reads. noteHashReadRequestPartialWitnesses: Array.from({ length: MAX_NOTE_HASH_READ_REQUESTS_PER_CALL }, () => @@ -82,12 +84,12 @@ describe('Kernel Prover', () => { const createProofOutput = (newNoteIndices: number[]) => { const publicInputs = PrivateKernelCircuitPublicInputs.empty(); - const commitments = makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, () => SideEffect.empty()); + const noteHashes = makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, NoteHashContext.empty); for (let i = 0; i < newNoteIndices.length; i++) { - commitments[i] = new SideEffect(generateFakeSiloedCommitment(notesAndSlots[newNoteIndices[i]]), Fr.ZERO); + noteHashes[i] = new NoteHashContext(generateFakeSiloedCommitment(notesAndSlots[newNoteIndices[i]]), 0, 0); } - publicInputs.end.newNoteHashes = commitments; + publicInputs.end.newNoteHashes = noteHashes; return { publicInputs, proof: makeEmptyProof(), diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index 000d26e72e4..1f036f3e089 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -1,13 +1,9 @@ import { CallRequest, Fr, - type MAX_ENCRYPTED_LOGS_PER_TX, - type MAX_NEW_NOTE_HASHES_PER_TX, - type MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - type MAX_UNENCRYPTED_LOGS_PER_TX, NoteHashReadRequestMembershipWitness, PrivateCallData, PrivateKernelCircuitPublicInputs, @@ -15,8 +11,6 @@ import { PrivateKernelInitCircuitPrivateInputs, PrivateKernelInnerCircuitPrivateInputs, PrivateKernelTailCircuitPrivateInputs, - type SideEffect, - type SideEffectLinkedToNoteHash, type TxRequest, VK_TREE_HEIGHT, VerificationKey, @@ -25,11 +19,15 @@ import { import { makeTuple } from '@aztec/foundation/array'; import { padArrayEnd } from '@aztec/foundation/collection'; import { createDebugLogger } from '@aztec/foundation/log'; -import { assertLength, mapTuple } from '@aztec/foundation/serialize'; +import { assertLength } from '@aztec/foundation/serialize'; import { pushTestData } from '@aztec/foundation/testing'; -import { type ExecutionResult } from '@aztec/simulator'; +import { type ExecutionResult, collectNullifiedNoteHashCounters } from '@aztec/simulator'; -import { HintsBuilder } from './hints_builder.js'; +import { + buildPrivateKernelInnerHints, + buildPrivateKernelTailHints, + buildPrivateKernelTailOutputs, +} from './private_inputs_builders/index.js'; import { KernelProofCreator, type ProofCreator, type ProofOutput, type ProofOutputFinal } from './proof_creator.js'; import { type ProvingDataOracle } from './proving_data_oracle.js'; @@ -41,11 +39,7 @@ import { type ProvingDataOracle } from './proving_data_oracle.js'; */ export class KernelProver { private log = createDebugLogger('aztec:kernel-prover'); - private hintsBuilder: HintsBuilder; - - constructor(private oracle: ProvingDataOracle, private proofCreator: ProofCreator = new KernelProofCreator()) { - this.hintsBuilder = new HintsBuilder(oracle); - } + constructor(private oracle: ProvingDataOracle, private proofCreator: ProofCreator = new KernelProofCreator()) {} /** * Generate a proof for a given transaction request and execution result. @@ -67,6 +61,11 @@ export class KernelProver { proof: makeEmptyProof(), }; + const noteHashNullifierCounterMap = new Map(); + collectNullifiedNoteHashCounters(executionResult).forEach(({ noteHashCounter, nullifierCounter }) => + noteHashNullifierCounterMap.set(noteHashCounter, nullifierCounter), + ); + while (executionStack.length) { const currentExecution = executionStack.pop()!; executionStack.push(...currentExecution.nestedExecutions); @@ -110,8 +109,13 @@ export class KernelProver { noteHashReadRequestMembershipWitnesses, ); + const hints = buildPrivateKernelInnerHints( + currentExecution.callStackItem.publicInputs, + noteHashNullifierCounterMap, + ); + if (firstIteration) { - const proofInput = new PrivateKernelInitCircuitPrivateInputs(txRequest, privateCallData); + const proofInput = new PrivateKernelInitCircuitPrivateInputs(txRequest, privateCallData, hints); pushTestData('private-kernel-inputs-init', proofInput); output = await this.proofCreator.createProofInit(proofInput); } else { @@ -123,7 +127,7 @@ export class KernelProver { Number(previousVkMembershipWitness.leafIndex), assertLength(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT), ); - const proofInput = new PrivateKernelInnerCircuitPrivateInputs(previousKernelData, privateCallData); + const proofInput = new PrivateKernelInnerCircuitPrivateInputs(previousKernelData, privateCallData, hints); pushTestData('private-kernel-inputs-inner', proofInput); output = await this.proofCreator.createProofInner(proofInput); } @@ -140,63 +144,16 @@ export class KernelProver { assertLength(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT), ); - const readNoteHashHints = this.hintsBuilder.getNoteHashReadRequestHints( - output.publicInputs.validationRequests.noteHashReadRequests, - output.publicInputs.end.newNoteHashes, - ); - - const nullifierReadRequestHints = await this.hintsBuilder.getNullifierReadRequestHints( - output.publicInputs.validationRequests.nullifierReadRequests, - output.publicInputs.end.newNullifiers, - ); - - const masterNullifierSecretKeys = await this.hintsBuilder.getMasterNullifierSecretKeys( - output.publicInputs.validationRequests.nullifierKeyValidationRequests, + this.log.debug( + `Calling private kernel tail with hwm ${previousKernelData.publicInputs.minRevertibleSideEffectCounter}`, ); - const [sortedNoteHashes, sortedNoteHashesIndexes] = this.hintsBuilder.sortSideEffects< - SideEffect, - typeof MAX_NEW_NOTE_HASHES_PER_TX - >(output.publicInputs.end.newNoteHashes); - - const [sortedNullifiers, sortedNullifiersIndexes] = this.hintsBuilder.sortSideEffects< - SideEffectLinkedToNoteHash, - typeof MAX_NEW_NULLIFIERS_PER_TX - >(output.publicInputs.end.newNullifiers); - - const [sortedEncryptedLogHashes, sortedEncryptedLogHashesIndexes] = this.hintsBuilder.sortSideEffects< - SideEffect, - typeof MAX_ENCRYPTED_LOGS_PER_TX - >(output.publicInputs.end.encryptedLogsHashes); + const hints = await buildPrivateKernelTailHints(output.publicInputs, this.oracle); - const [sortedUnencryptedLogHashes, sortedUnencryptedLogHashesIndexes] = this.hintsBuilder.sortSideEffects< - SideEffect, - typeof MAX_UNENCRYPTED_LOGS_PER_TX - >(output.publicInputs.end.unencryptedLogsHashes); + const expectedOutputs = buildPrivateKernelTailOutputs(hints.sortedNewNoteHashes, hints.sortedNewNullifiers); - const nullifierNoteHashHints = this.hintsBuilder.getNullifierHints( - mapTuple(sortedNullifiers, n => n.noteHash), - sortedNoteHashes, - ); - this.log.debug( - `Calling private kernel tail with hwm ${previousKernelData.publicInputs.minRevertibleSideEffectCounter}`, - ); + const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData, expectedOutputs, hints); - const privateInputs = new PrivateKernelTailCircuitPrivateInputs( - previousKernelData, - sortedNoteHashes, - sortedNoteHashesIndexes, - readNoteHashHints, - sortedNullifiers, - sortedNullifiersIndexes, - nullifierReadRequestHints, - nullifierNoteHashHints, - sortedEncryptedLogHashes, - sortedEncryptedLogHashesIndexes, - sortedUnencryptedLogHashes, - sortedUnencryptedLogHashesIndexes, - masterNullifierSecretKeys, - ); pushTestData('private-kernel-inputs-ordering', privateInputs); return await this.proofCreator.createProofTail(privateInputs); } diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.ts new file mode 100644 index 00000000000..a03ac365947 --- /dev/null +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.ts @@ -0,0 +1,17 @@ +import { + type MAX_NEW_NOTE_HASHES_PER_CALL, + type PrivateCircuitPublicInputs, + PrivateKernelInnerHints, +} from '@aztec/circuits.js'; +import { type Tuple } from '@aztec/foundation/serialize'; + +export function buildPrivateKernelInnerHints( + publicInputs: PrivateCircuitPublicInputs, + noteHashNullifierCounterMap: Map, +) { + const nullifierCounters = publicInputs.newNoteHashes.map( + n => noteHashNullifierCounterMap.get(n.counter) ?? 0, + ) as Tuple; + + return new PrivateKernelInnerHints(nullifierCounters); +} diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts new file mode 100644 index 00000000000..5fcadf94703 --- /dev/null +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts @@ -0,0 +1,194 @@ +import { + type Fr, + GrumpkinScalar, + type MAX_ENCRYPTED_LOGS_PER_TX, + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NOTE_HASH_READ_REQUESTS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + type MAX_NULLIFIER_READ_REQUESTS_PER_TX, + type MAX_UNENCRYPTED_LOGS_PER_TX, + MembershipWitness, + NULLIFIER_TREE_HEIGHT, + type NoteHashContext, + type Nullifier, + type NullifierKeyValidationRequestContext, + type PrivateKernelCircuitPublicInputs, + PrivateKernelTailHints, + type ReadRequestContext, + type SideEffect, + type SideEffectType, + buildNullifierReadRequestHints, + buildTransientDataHints, + countAccumulatedItems, + sortByCounterGetSortedHints, +} from '@aztec/circuits.js'; +import { makeTuple } from '@aztec/foundation/array'; +import { type Tuple } from '@aztec/foundation/serialize'; + +import { type ProvingDataOracle } from '../proving_data_oracle.js'; + +/** @deprecated Use sortByCounterGetSortedHints instead */ +function sortSideEffects( + sideEffects: Tuple, +): [Tuple, Tuple] { + const sorted = sideEffects + .map((sideEffect, index) => ({ sideEffect, index })) + .sort((a, b) => { + // Empty ones go to the right + if (a.sideEffect.isEmpty()) { + return 1; + } + return Number(a.sideEffect.counter.toBigInt() - b.sideEffect.counter.toBigInt()); + }); + + const originalToSorted = sorted.map(() => 0); + sorted.forEach(({ index }, i) => { + originalToSorted[index] = i; + }); + + return [sorted.map(({ sideEffect }) => sideEffect) as Tuple, originalToSorted as Tuple]; +} + +function isValidNoteHashReadRequest(readRequest: SideEffect, noteHash: NoteHashContext) { + return ( + noteHash.value.equals(readRequest.value) && + noteHash.counter < readRequest.counter.toNumber() && + (noteHash.nullifierCounter === 0 || noteHash.nullifierCounter > readRequest.counter.toNumber()) + ); +} + +/** + * Performs the matching between an array of read request and an array of note hashes. This produces + * hints for the private kernel tail circuit to efficiently match a read request with the corresponding + * note hash. Several read requests might be pointing to the same note hash. It is therefore valid + * to return more than one hint with the same index. + * + * @param noteHashReadRequests - The array of read requests. + * @param noteHashes - The array of note hashes. + * @returns An array of hints where each element is the index of the note hash in note hashes array + * corresponding to the read request. In other words we have readRequests[i] == noteHashes[hints[i]]. + */ +function getNoteHashReadRequestHints( + noteHashReadRequests: Tuple, + noteHashes: Tuple, +): Tuple { + const hints = makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, () => 0); + const numReadRequests = countAccumulatedItems(noteHashReadRequests); + for (let i = 0; i < numReadRequests; i++) { + const readRequest = noteHashReadRequests[i]; + const noteHashIndex = noteHashes.findIndex((n: NoteHashContext) => isValidNoteHashReadRequest(readRequest, n)); + if (noteHashIndex === -1) { + throw new Error(`The read request at index ${i} ${readRequest} does not match to any note hash.`); + } + hints[i] = noteHashIndex; + } + return hints; +} + +function getNullifierReadRequestHints( + nullifierReadRequests: Tuple, + nullifiers: Tuple, + oracle: ProvingDataOracle, +) { + const getNullifierMembershipWitness = async (nullifier: Fr) => { + const res = await oracle.getNullifierMembershipWitness(nullifier); + if (!res) { + throw new Error(`Cannot find the leaf for nullifier ${nullifier.toBigInt()}.`); + } + + const { index, siblingPath, leafPreimage } = res; + return { + membershipWitness: new MembershipWitness( + NULLIFIER_TREE_HEIGHT, + index, + siblingPath.toTuple(), + ), + leafPreimage, + }; + }; + + return buildNullifierReadRequestHints({ getNullifierMembershipWitness }, nullifierReadRequests, nullifiers); +} + +async function getMasterNullifierSecretKeys( + nullifierKeyValidationRequests: Tuple< + NullifierKeyValidationRequestContext, + typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + >, + oracle: ProvingDataOracle, +) { + const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero); + for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) { + const request = nullifierKeyValidationRequests[i]; + if (request.isEmpty()) { + break; + } + keys[i] = await oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey); + } + return keys; +} + +export async function buildPrivateKernelTailHints( + publicInputs: PrivateKernelCircuitPublicInputs, + oracle: ProvingDataOracle, +) { + const noteHashReadRequestHints = getNoteHashReadRequestHints( + publicInputs.validationRequests.noteHashReadRequests, + publicInputs.end.newNoteHashes, + ); + + const nullifierReadRequestHints = await getNullifierReadRequestHints( + publicInputs.validationRequests.nullifierReadRequests, + publicInputs.end.newNullifiers, + oracle, + ); + + const masterNullifierSecretKeys = await getMasterNullifierSecretKeys( + publicInputs.validationRequests.nullifierKeyValidationRequests, + oracle, + ); + + const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints( + publicInputs.end.newNoteHashes, + MAX_NEW_NOTE_HASHES_PER_TX, + ); + + const [sortedNullifiers, sortedNullifiersIndexes] = sortByCounterGetSortedHints( + publicInputs.end.newNullifiers, + MAX_NEW_NULLIFIERS_PER_TX, + ); + + const [sortedEncryptedLogHashes, sortedEncryptedLogHashesIndexes] = sortSideEffects< + SideEffect, + typeof MAX_ENCRYPTED_LOGS_PER_TX + >(publicInputs.end.encryptedLogsHashes); + + const [sortedUnencryptedLogHashes, sortedUnencryptedLogHashesIndexes] = sortSideEffects< + SideEffect, + typeof MAX_UNENCRYPTED_LOGS_PER_TX + >(publicInputs.end.unencryptedLogsHashes); + + const [transientNullifierIndexesForNoteHashes, transientNoteHashIndexesForNullifiers] = buildTransientDataHints( + sortedNoteHashes, + sortedNullifiers, + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + ); + + return new PrivateKernelTailHints( + transientNullifierIndexesForNoteHashes, + transientNoteHashIndexesForNullifiers, + noteHashReadRequestHints, + nullifierReadRequestHints, + masterNullifierSecretKeys, + sortedNoteHashes, + sortedNoteHashesIndexes, + sortedNullifiers, + sortedNullifiersIndexes, + sortedEncryptedLogHashes, + sortedEncryptedLogHashesIndexes, + sortedUnencryptedLogHashes, + sortedUnencryptedLogHashesIndexes, + ); +} diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts new file mode 100644 index 00000000000..ab2594b7b62 --- /dev/null +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts @@ -0,0 +1,30 @@ +import { + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + NoteHashContext, + Nullifier, + PrivateKernelTailOutputs, +} from '@aztec/circuits.js'; +import { padArrayEnd } from '@aztec/foundation/collection'; +import { type Tuple } from '@aztec/foundation/serialize'; + +export function buildPrivateKernelTailOutputs( + prevNoteHashes: Tuple, + prevNullifiers: Tuple, +) { + // Propagate note hashes that are not linked to a nullifier. + // Note that note hashes can't link to the first nullifier (counter == 0). + const noteHashes = padArrayEnd( + prevNoteHashes.filter(n => !n.nullifierCounter), + NoteHashContext.empty(), + MAX_NEW_NOTE_HASHES_PER_TX, + ); + + const nullifiers = padArrayEnd( + prevNullifiers.filter(n => n.noteHash.isZero()), + Nullifier.empty(), + MAX_NEW_NULLIFIERS_PER_TX, + ); + + return new PrivateKernelTailOutputs(noteHashes, nullifiers); +} diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts new file mode 100644 index 00000000000..ae00ec7a4f0 --- /dev/null +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts @@ -0,0 +1,3 @@ +export { buildPrivateKernelInnerHints } from './build_private_kernel_inner_hints.js'; +export { buildPrivateKernelTailHints } from './build_private_kernel_tail_hints.js'; +export { buildPrivateKernelTailOutputs } from './build_private_kernel_tail_outputs.js'; diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index f112db7d65a..a5787a406d5 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -238,18 +238,24 @@ export class Oracle { [noteTypeId]: ACVMField[], note: ACVMField[], [innerNoteHash]: ACVMField[], + [counter]: ACVMField[], ): ACVMField { this.typedOracle.notifyCreatedNote( fromACVMField(storageSlot), fromACVMField(noteTypeId), note.map(fromACVMField), fromACVMField(innerNoteHash), + +counter, ); return toACVMField(0); } - async notifyNullifiedNote([innerNullifier]: ACVMField[], [innerNoteHash]: ACVMField[]): Promise { - await this.typedOracle.notifyNullifiedNote(fromACVMField(innerNullifier), fromACVMField(innerNoteHash)); + async notifyNullifiedNote( + [innerNullifier]: ACVMField[], + [innerNoteHash]: ACVMField[], + [counter]: ACVMField[], + ): Promise { + await this.typedOracle.notifyNullifiedNote(fromACVMField(innerNullifier), fromACVMField(innerNoteHash), +counter); return toACVMField(0); } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 0771458a972..360607ee5f6 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -159,11 +159,11 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('getNotes'); } - notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr): void { + notifyCreatedNote(_storageSlot: Fr, _noteTypeId: Fr, _note: Fr[], _innerNoteHash: Fr, _counter: number): void { throw new OracleMethodNotAvailableError('notifyCreatedNote'); } - notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr): Promise { + notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { throw new OracleMethodNotAvailableError('notifyNullifiedNote'); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 0bcb1f07dbb..57a79776a97 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -32,7 +32,7 @@ import { type NoteData, toACVMWitness } from '../acvm/index.js'; import { type PackedValuesCache } from '../common/packed_values_cache.js'; import { type DBOracle } from './db_oracle.js'; import { type ExecutionNoteCache } from './execution_note_cache.js'; -import { type ExecutionResult, type NoteAndSlot } from './execution_result.js'; +import { type ExecutionResult, type NoteAndSlot, type NullifiedNoteHashCounter } from './execution_result.js'; import { pickNotes } from './pick_notes.js'; import { executePrivateFunction } from './private_execution.js'; import { ViewDataOracle } from './view_data_oracle.js'; @@ -59,6 +59,7 @@ export class ClientExecutionContext extends ViewDataOracle { * They should act as references for the read requests output by an app circuit via public inputs. */ private gotNotes: Map = new Map(); + private nullifiedNoteHashCounters: NullifiedNoteHashCounter[] = []; private encryptedLogs: EncryptedL2Log[] = []; private unencryptedLogs: UnencryptedL2Log[] = []; private nestedExecutions: ExecutionResult[] = []; @@ -139,6 +140,10 @@ export class ClientExecutionContext extends ViewDataOracle { return this.newNotes; } + public getNullifiedNoteHashCounters() { + return this.nullifiedNoteHashCounters; + } + /** * Return the encrypted logs emitted during this execution. */ @@ -278,16 +283,25 @@ export class ClientExecutionContext extends ViewDataOracle { * @param innerNoteHash - The inner note hash of the new note. * @returns */ - public override notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr) { + public override notifyCreatedNote( + storageSlot: Fr, + noteTypeId: Fr, + noteItems: Fr[], + innerNoteHash: Fr, + counter: number, + ) { const note = new Note(noteItems); - this.noteCache.addNewNote({ - contractAddress: this.callContext.storageContractAddress, - storageSlot, - nonce: Fr.ZERO, // Nonce cannot be known during private execution. - note, - siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. - innerNoteHash, - }); + this.noteCache.addNewNote( + { + contractAddress: this.callContext.storageContractAddress, + storageSlot, + nonce: Fr.ZERO, // Nonce cannot be known during private execution. + note, + siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. + innerNoteHash, + }, + counter, + ); this.newNotes.push({ storageSlot, noteTypeId, @@ -301,8 +315,15 @@ export class ClientExecutionContext extends ViewDataOracle { * @param innerNullifier - The pending nullifier to add in the list (not yet siloed by contract address). * @param innerNoteHash - The inner note hash of the new note. */ - public override notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr) { - this.noteCache.nullifyNote(this.callContext.storageContractAddress, innerNullifier, innerNoteHash); + public override notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, counter: number) { + const nullifiedNoteHashCounter = this.noteCache.nullifyNote( + this.callContext.storageContractAddress, + innerNullifier, + innerNoteHash, + ); + if (nullifiedNoteHashCounter !== undefined) { + this.nullifiedNoteHashCounters.push({ noteHashCounter: nullifiedNoteHashCounter, nullifierCounter: counter }); + } return Promise.resolve(); } diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index 67752a52a29..2166f317941 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -4,6 +4,11 @@ import { Fr } from '@aztec/foundation/fields'; import { type NoteData } from '../acvm/index.js'; +export interface PendingNote { + note: NoteData; + counter: number; +} + /** * Data that's accessible by all the function calls in an execution. */ @@ -12,7 +17,7 @@ export class ExecutionNoteCache { * New notes created in this transaction. * This mapping maps from a contract address to the notes in the contract. */ - private newNotes: Map = new Map(); + private newNotes: Map = new Map(); /** * The list of nullifiers created in this transaction. @@ -26,9 +31,9 @@ export class ExecutionNoteCache { * Add a new note to cache. * @param note - New note created during execution. */ - public addNewNote(note: NoteData) { + public addNewNote(note: NoteData, counter: number) { const notes = this.newNotes.get(note.contractAddress.toBigInt()) ?? []; - notes.push(note); + notes.push({ note, counter }); this.newNotes.set(note.contractAddress.toBigInt(), notes); } @@ -46,16 +51,20 @@ export class ExecutionNoteCache { nullifiers.add(siloedNullifier.value); this.nullifiers.set(contractAddress.toBigInt(), nullifiers); + let nullifiedNoteHashCounter: number | undefined = undefined; // Find and remove the matching new note if the emitted innerNoteHash is not empty. if (!innerNoteHash.equals(Fr.ZERO)) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - const noteIndexToRemove = notes.findIndex(n => n.innerNoteHash.equals(innerNoteHash)); + const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash)); if (noteIndexToRemove === -1) { throw new Error('Attempt to remove a pending note that does not exist.'); } - notes.splice(noteIndexToRemove, 1); + const note = notes.splice(noteIndexToRemove, 1)[0]; + nullifiedNoteHashCounter = note.counter; this.newNotes.set(contractAddress.toBigInt(), notes); } + + return nullifiedNoteHashCounter; } /** @@ -66,7 +75,7 @@ export class ExecutionNoteCache { **/ public getNotes(contractAddress: AztecAddress, storageSlot: Fr) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - return notes.filter(n => n.storageSlot.equals(storageSlot)); + return notes.filter(n => n.note.storageSlot.equals(storageSlot)).map(n => n.note); } /** @@ -77,7 +86,7 @@ export class ExecutionNoteCache { **/ public checkNoteExists(contractAddress: AztecAddress, innerNoteHash: Fr) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - return notes.some(n => n.innerNoteHash.equals(innerNoteHash)); + return notes.some(n => n.note.innerNoteHash.equals(innerNoteHash)); } /** diff --git a/yarn-project/simulator/src/client/execution_result.test.ts b/yarn-project/simulator/src/client/execution_result.test.ts index 0938c50af58..a3cc96e6cc9 100644 --- a/yarn-project/simulator/src/client/execution_result.test.ts +++ b/yarn-project/simulator/src/client/execution_result.test.ts @@ -17,6 +17,7 @@ function emptyExecutionResult(): ExecutionResult { callStackItem: PrivateCallStackItem.empty(), noteHashReadRequestPartialWitnesses: [], newNotes: [], + nullifiedNoteHashCounters: [], returnValues: [], nestedExecutions: [], enqueuedPublicFunctionCalls: [], diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index 845386e9807..b2dc1eb3fe4 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -20,6 +20,11 @@ export interface NoteAndSlot { noteTypeId: Fr; } +export interface NullifiedNoteHashCounter { + noteHashCounter: number; + nullifierCounter: number; +} + /** * The result of executing a private function. */ @@ -36,9 +41,9 @@ export interface ExecutionResult { callStackItem: PrivateCallStackItem; /** The partially filled-in read request membership witnesses for commitments being read. */ noteHashReadRequestPartialWitnesses: NoteHashReadRequestMembershipWitness[]; - // Needed when we enable chained txs. The new notes can be cached and used in a later transaction. /** The notes created in the executed function. */ newNotes: NoteAndSlot[]; + nullifiedNoteHashCounters: NullifiedNoteHashCounter[]; /** The raw return values of the executed function. */ returnValues: Fr[]; /** The nested executions. */ @@ -57,6 +62,13 @@ export interface ExecutionResult { unencryptedLogs: UnencryptedFunctionL2Logs; } +export function collectNullifiedNoteHashCounters(execResult: ExecutionResult): NullifiedNoteHashCounter[] { + return [ + execResult.nullifiedNoteHashCounters, + ...execResult.nestedExecutions.flatMap(collectNullifiedNoteHashCounters), + ].flat(); +} + /** * Collect all encrypted logs across all nested executions. * @param execResult - The topmost execution result. diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index ba7163432b2..5dbd922a9c6 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -18,8 +18,7 @@ import { computeAppNullifierSecretKey, deriveKeys, getContractInstanceFromDeployParams, - nonEmptySideEffects, - sideEffectArrayToValueArray, + getNonEmptyItems, } from '@aztec/circuits.js'; import { computeCommitmentNonce, computeSecretHash, computeVarArgsHash } from '@aztec/circuits.js/hash'; import { makeHeader } from '@aztec/circuits.js/testing'; @@ -294,13 +293,9 @@ describe('Private Execution test suite', () => { expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); expect(newNote.noteTypeId).toEqual(new Fr(869710811710178111116101n)); // ValueNote - const newNoteHashes = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNoteHashes), - ); + const newNoteHashes = getNonEmptyItems(result.callStackItem.publicInputs.newNoteHashes); expect(newNoteHashes).toHaveLength(1); - - const [commitment] = newNoteHashes; - expect(commitment).toEqual( + expect(newNoteHashes[0].value).toEqual( await acirSimulator.computeInnerNoteHash( contractAddress, newNote.storageSlot, @@ -320,13 +315,9 @@ describe('Private Execution test suite', () => { expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); expect(newNote.noteTypeId).toEqual(new Fr(869710811710178111116101n)); // ValueNote - const newNoteHashes = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNoteHashes), - ); + const newNoteHashes = getNonEmptyItems(result.callStackItem.publicInputs.newNoteHashes); expect(newNoteHashes).toHaveLength(1); - - const [commitment] = newNoteHashes; - expect(commitment).toEqual( + expect(newNoteHashes[0].value).toEqual( await acirSimulator.computeInnerNoteHash( contractAddress, newNote.storageSlot, @@ -360,9 +351,7 @@ describe('Private Execution test suite', () => { const result = await runSimulator({ args, artifact, msgSender: owner }); // The two notes were nullified - const newNullifiers = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNullifiers), - ); + const newNullifiers = getNonEmptyItems(result.callStackItem.publicInputs.newNullifiers).map(n => n.value); expect(newNullifiers).toHaveLength(consumedNotes.length); expect(newNullifiers).toEqual(expect.arrayContaining(consumedNotes.map(n => n.innerNullifier))); @@ -371,26 +360,20 @@ describe('Private Execution test suite', () => { expect(recipientNote.storageSlot).toEqual(recipientStorageSlot); expect(recipientNote.noteTypeId).toEqual(noteTypeId); - const newNoteHashes = sideEffectArrayToValueArray(result.callStackItem.publicInputs.newNoteHashes).filter( - field => !field.equals(Fr.ZERO), - ); + const newNoteHashes = getNonEmptyItems(result.callStackItem.publicInputs.newNoteHashes); expect(newNoteHashes).toHaveLength(2); - - const [changeNoteCommitment, recipientNoteCommitment] = newNoteHashes; - expect(recipientNoteCommitment).toEqual( + const [changeNoteHash, recipientNoteHash] = newNoteHashes; + expect(recipientNoteHash.value).toEqual( await acirSimulator.computeInnerNoteHash(contractAddress, recipientStorageSlot, noteTypeId, recipientNote.note), ); - expect(changeNoteCommitment).toEqual( + expect(changeNoteHash.value).toEqual( await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, noteTypeId, changeNote.note), ); expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); expect(changeNote.note.items[0]).toEqual(new Fr(40n)); - const readRequests = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.noteHashReadRequests), - ); - + const readRequests = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests).map(r => r.value); expect(readRequests).toHaveLength(consumedNotes.length); expect(readRequests).toEqual(expect.arrayContaining(consumedNotes.map(n => n.uniqueSiloedNoteHash))); }); @@ -414,9 +397,7 @@ describe('Private Execution test suite', () => { const args = [recipient, amountToTransfer]; const result = await runSimulator({ args, artifact, msgSender: owner }); - const newNullifiers = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNullifiers), - ); + const newNullifiers = getNonEmptyItems(result.callStackItem.publicInputs.newNullifiers).map(n => n.value); expect(newNullifiers).toEqual(consumedNotes.map(n => n.innerNullifier)); expect(result.newNotes).toHaveLength(2); @@ -582,10 +563,7 @@ describe('Private Execution test suite', () => { }); // Check a nullifier has been inserted - const newNullifiers = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNullifiers), - ); - + const newNullifiers = getNonEmptyItems(result.callStackItem.publicInputs.newNullifiers); expect(newNullifiers).toHaveLength(1); }); @@ -749,17 +727,11 @@ describe('Private Execution test suite', () => { const result = await runSimulator({ artifact, args: [secret] }); // Check a nullifier has been inserted. - const newNullifiers = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNullifiers), - ); - + const newNullifiers = getNonEmptyItems(result.callStackItem.publicInputs.newNullifiers); expect(newNullifiers).toHaveLength(1); // Check the commitment read request was created successfully. - const readRequests = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.noteHashReadRequests), - ); - + const readRequests = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests); expect(readRequests).toHaveLength(1); }); }); @@ -859,12 +831,10 @@ describe('Private Execution test suite', () => { expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); - const newNoteHashes = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNoteHashes), - ); + const newNoteHashes = getNonEmptyItems(result.callStackItem.publicInputs.newNoteHashes); expect(newNoteHashes).toHaveLength(1); - const noteHash = newNoteHashes[0]; + const noteHash = newNoteHashes[0].value; const storageSlot = computeSlotForMapping( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, @@ -880,8 +850,8 @@ describe('Private Execution test suite', () => { expect(noteHash).toEqual(innerNoteHash); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) - const readRequest = sideEffectArrayToValueArray(result.callStackItem.publicInputs.noteHashReadRequests)[0]; - expect(readRequest).toEqual(innerNoteHash); + const readRequest = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests)[0]; + expect(readRequest.value).toEqual(innerNoteHash); expect(result.returnValues).toEqual([new Fr(amountToTransfer)]); @@ -937,12 +907,10 @@ describe('Private Execution test suite', () => { expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); - const newNoteHashes = sideEffectArrayToValueArray( - nonEmptySideEffects(execInsert.callStackItem.publicInputs.newNoteHashes), - ); + const newNoteHashes = getNonEmptyItems(execInsert.callStackItem.publicInputs.newNoteHashes); expect(newNoteHashes).toHaveLength(1); - const noteHash = newNoteHashes[0]; + const noteHash = newNoteHashes[0].value; const innerNoteHash = await acirSimulator.computeInnerNoteHash( contractAddress, noteAndSlot.storageSlot, diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index 64cf9baae7d..8c3174f0add 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -57,6 +57,7 @@ export async function executePrivateFunction( publicInputs.noteHashReadRequests, ); const newNotes = context.getNewNotes(); + const nullifiedNoteHashCounters = context.getNullifiedNoteHashCounters(); const nestedExecutions = context.getNestedExecutions(); const enqueuedPublicFunctionCalls = context.getEnqueuedPublicFunctionCalls(); @@ -69,6 +70,7 @@ export async function executePrivateFunction( returnValues: rawReturnValues, noteHashReadRequestPartialWitnesses, newNotes, + nullifiedNoteHashCounters, vk: Buffer.from(artifact.verificationKey!, 'hex'), nestedExecutions, enqueuedPublicFunctionCalls, diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index f84298d6dea..00997d4acd2 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -29,6 +29,8 @@ import { MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_CALL, MembershipWitness, + NoteHash, + Nullifier, type PrivateKernelTailCircuitPublicInputs, type Proof, PublicCallData, @@ -43,7 +45,6 @@ import { ReadRequest, RevertCode, SideEffect, - SideEffectLinkedToNoteHash, VK_TREE_HEIGHT, VerificationKey, makeEmptyProof, @@ -387,8 +388,8 @@ export abstract class AbstractPhaseManager { callContext: result.execution.callContext, proverAddress: AztecAddress.ZERO, argsHash: computeVarArgsHash(result.execution.args), - newNoteHashes: padArrayEnd(result.newNoteHashes, SideEffect.empty(), MAX_NEW_NOTE_HASHES_PER_CALL), - newNullifiers: padArrayEnd(result.newNullifiers, SideEffectLinkedToNoteHash.empty(), MAX_NEW_NULLIFIERS_PER_CALL), + newNoteHashes: padArrayEnd(result.newNoteHashes, NoteHash.empty(), MAX_NEW_NOTE_HASHES_PER_CALL), + newNullifiers: padArrayEnd(result.newNullifiers, Nullifier.empty(), MAX_NEW_NULLIFIERS_PER_CALL), newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, L2ToL1Message.empty(), MAX_NEW_L2_TO_L1_MSGS_PER_CALL), startSideEffectCounter: result.startSideEffectCounter, endSideEffectCounter: result.endSideEffectCounter, diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index 25523f24a67..11db9dd0b5e 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -4,12 +4,13 @@ import { type ContractStorageUpdateRequest, type Fr, type L2ToL1Message, + type NoteHash, + type Nullifier, type PublicCallRequest, PublicDataRead, PublicDataUpdateRequest, type ReadRequest, type SideEffect, - type SideEffectLinkedToNoteHash, } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot, computePublicDataTreeValue } from '@aztec/circuits.js/hash'; @@ -24,7 +25,7 @@ export interface PublicExecutionResult { /** The return values of the function. */ returnValues: Fr[]; /** The new note hashes to be inserted into the note hashes tree. */ - newNoteHashes: SideEffect[]; + newNoteHashes: NoteHash[]; /** The new l2 to l1 messages generated in this call. */ newL2ToL1Messages: L2ToL1Message[]; /** The side effect counter at the start of the function call. */ @@ -32,7 +33,7 @@ export interface PublicExecutionResult { /** The side effect counter after executing this function call */ endSideEffectCounter: Fr; /** The new nullifiers to be inserted into the nullifier tree. */ - newNullifiers: SideEffectLinkedToNoteHash[]; + newNullifiers: Nullifier[]; /** The nullifier read requests emitted in this call. */ nullifierReadRequests: ReadRequest[]; /** The nullifier non existent read requests emitted in this call. */ @@ -157,8 +158,8 @@ function contractStorageUpdateRequestToPublicDataUpdateRequest( */ export function checkValidStaticCall( - newNoteHashes: SideEffect[], - newNullifiers: SideEffectLinkedToNoteHash[], + newNoteHashes: NoteHash[], + newNullifiers: Nullifier[], contractStorageUpdateRequests: ContractStorageUpdateRequest[], newL2ToL1Messages: L2ToL1Message[], unencryptedLogs: UnencryptedFunctionL2Logs, diff --git a/yarn-project/simulator/src/public/hints_builder.ts b/yarn-project/simulator/src/public/hints_builder.ts index 5cc4988fd6f..b0cb14e33fb 100644 --- a/yarn-project/simulator/src/public/hints_builder.ts +++ b/yarn-project/simulator/src/public/hints_builder.ts @@ -9,13 +9,13 @@ import { type MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MembershipWitness, NULLIFIER_TREE_HEIGHT, + type Nullifier, PUBLIC_DATA_TREE_HEIGHT, type PublicDataHint, type PublicDataRead, type PublicDataTreeLeafPreimage, type PublicDataUpdateRequest, type ReadRequestContext, - type SideEffectLinkedToNoteHash, buildNullifierNonExistentReadRequestHints, buildNullifierReadRequestHints, buildPublicDataHints, @@ -29,14 +29,14 @@ export class HintsBuilder { getNullifierReadRequestHints( nullifierReadRequests: Tuple, - pendingNullifiers: Tuple, + pendingNullifiers: Tuple, ) { return buildNullifierReadRequestHints(this, nullifierReadRequests, pendingNullifiers); } getNullifierNonExistentReadRequestHints( nullifierNonExistentReadRequests: Tuple, - pendingNullifiers: Tuple, + pendingNullifiers: Tuple, ) { return buildNullifierNonExistentReadRequestHints(this, nullifierNonExistentReadRequests, pendingNullifiers); } diff --git a/yarn-project/simulator/src/public/tail_phase_manager.ts b/yarn-project/simulator/src/public/tail_phase_manager.ts index c5c3487f1a9..e60b1d42a06 100644 --- a/yarn-project/simulator/src/public/tail_phase_manager.ts +++ b/yarn-project/simulator/src/public/tail_phase_manager.ts @@ -14,6 +14,7 @@ import { MAX_NEW_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, type MAX_UNENCRYPTED_LOGS_PER_TX, + type NoteHash, type Proof, type PublicKernelCircuitPublicInputs, PublicKernelTailCircuitPrivateInputs, @@ -89,9 +90,9 @@ export class TailPhaseManager extends AbstractPhaseManager { // Temporary hack. Should sort them in the tail circuit. const noteHashes = mergeAccumulatedData( - MAX_NEW_NOTE_HASHES_PER_TX, previousOutput.endNonRevertibleData.newNoteHashes, previousOutput.end.newNoteHashes, + MAX_NEW_NOTE_HASHES_PER_TX, ); output.end.newNoteHashes = this.sortNoteHashes(noteHashes); @@ -113,9 +114,9 @@ export class TailPhaseManager extends AbstractPhaseManager { const { validationRequests, endNonRevertibleData, end } = previousOutput; const pendingNullifiers = mergeAccumulatedData( - MAX_NEW_NULLIFIERS_PER_TX, endNonRevertibleData.newNullifiers, end.newNullifiers, + MAX_NEW_NULLIFIERS_PER_TX, ); const nullifierReadRequestHints = await this.hintsBuilder.getNullifierReadRequestHints( @@ -129,9 +130,9 @@ export class TailPhaseManager extends AbstractPhaseManager { ); const pendingPublicDataWrites = mergeAccumulatedData( - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, endNonRevertibleData.publicDataUpdateRequests, end.publicDataUpdateRequests, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, ); const publicDataHints = await this.hintsBuilder.getPublicDataHints( @@ -157,17 +158,14 @@ export class TailPhaseManager extends AbstractPhaseManager { ); } - private sortNoteHashes(noteHashes: Tuple): Tuple { - return sortByCounter(noteHashes.map(n => ({ ...n, counter: n.counter.toNumber() }))).map(n => n.value) as Tuple< - Fr, - N - >; + private sortNoteHashes(noteHashes: Tuple): Tuple { + return sortByCounter(noteHashes).map(n => n.value) as Tuple; } private sortLogsHashes(unencryptedLogsHashes: Tuple): Tuple { - return sortByCounter(unencryptedLogsHashes.map(n => ({ ...n, counter: n.counter.toNumber() }))).map( - h => new SideEffect(h.value, new Fr(h.counter)), - ) as Tuple; + return sortByCounter( + unencryptedLogsHashes.map(n => ({ ...n, counter: n.counter.toNumber(), isEmpty: () => n.isEmpty() })), + ).map(h => new SideEffect(h.value, new Fr(h.counter))) as Tuple; } // As above, this is a hack for unencrypted logs ordering, now they are sorted. Since the public kernel diff --git a/yarn-project/simulator/src/public/transitional_adaptors.ts b/yarn-project/simulator/src/public/transitional_adaptors.ts index a791e2ef7fc..3091deb1476 100644 --- a/yarn-project/simulator/src/public/transitional_adaptors.ts +++ b/yarn-project/simulator/src/public/transitional_adaptors.ts @@ -10,9 +10,10 @@ import { type GlobalVariables, type Header, L2ToL1Message, + NoteHash, + Nullifier, ReadRequest, SideEffect, - SideEffectLinkedToNoteHash, } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; @@ -124,7 +125,7 @@ export async function convertAvmResults( } const newNoteHashes = newWorldState.newNoteHashes.map( - noteHash => new SideEffect(noteHash.noteHash, noteHash.counter), + noteHash => new NoteHash(noteHash.noteHash, noteHash.counter.toNumber()), ); const nullifierReadRequests: ReadRequest[] = newWorldState.nullifierChecks .filter(nullifierCheck => nullifierCheck.exists) @@ -132,12 +133,12 @@ export async function convertAvmResults( const nullifierNonExistentReadRequests: ReadRequest[] = newWorldState.nullifierChecks .filter(nullifierCheck => !nullifierCheck.exists) .map(nullifierCheck => new ReadRequest(nullifierCheck.nullifier, nullifierCheck.counter.toNumber())); - const newNullifiers: SideEffectLinkedToNoteHash[] = newWorldState.newNullifiers.map( + const newNullifiers: Nullifier[] = newWorldState.newNullifiers.map( tracedNullifier => - new SideEffectLinkedToNoteHash( + new Nullifier( /*value=*/ tracedNullifier.nullifier, + tracedNullifier.counter.toNumber(), /*noteHash=*/ Fr.ZERO, // NEEDED? - tracedNullifier.counter, ), ); const unencryptedLogs: UnencryptedFunctionL2Logs = new UnencryptedFunctionL2Logs( @@ -216,7 +217,7 @@ export function updateAvmContextFromPublicExecutionResult(ctx: AvmContext, resul ctx.persistableState.trace.newNullifiers.push({ storageAddress: ctx.environment.storageAddress, nullifier: nullifier.value, - counter: nullifier.counter, + counter: new Fr(nullifier.counter), }); } @@ -224,7 +225,7 @@ export function updateAvmContextFromPublicExecutionResult(ctx: AvmContext, resul ctx.persistableState.trace.newNoteHashes.push({ storageAddress: ctx.environment.storageAddress, noteHash: noteHash.value, - counter: noteHash.counter, + counter: new Fr(noteHash.counter), }); } diff --git a/yarn-project/simulator/src/public/utils.test.ts b/yarn-project/simulator/src/public/utils.test.ts index 7186ed626eb..3f791162bcd 100644 --- a/yarn-project/simulator/src/public/utils.test.ts +++ b/yarn-project/simulator/src/public/utils.test.ts @@ -10,16 +10,16 @@ describe('sequencer utils', () => { // mockTx creates a Tx with side effect counts of all 0 expect(lastSideEffectCounter(tx)).toBe(0); - tx.data.forPublic!.endNonRevertibleData.newNoteHashes.at(-1)!.counter = new Fr(8); + tx.data.forPublic!.endNonRevertibleData.newNoteHashes.at(-1)!.counter = 8; expect(lastSideEffectCounter(tx)).toBe(8); tx.data.forPublic!.endNonRevertibleData.publicCallStack.at(-1)!.startSideEffectCounter = new Fr(9); expect(lastSideEffectCounter(tx)).toBe(9); - tx.data.forPublic!.end.newNoteHashes.at(-1)!.counter = new Fr(10); + tx.data.forPublic!.end.newNoteHashes.at(-1)!.counter = 10; expect(lastSideEffectCounter(tx)).toBe(10); - tx.data.forPublic!.end.newNullifiers.at(-1)!.counter = new Fr(11); + tx.data.forPublic!.end.newNullifiers.at(-1)!.counter = 11; expect(lastSideEffectCounter(tx)).toBe(11); }); }); diff --git a/yarn-project/simulator/src/public/utils.ts b/yarn-project/simulator/src/public/utils.ts index 7a2fae3b78e..c48798ff350 100644 --- a/yarn-project/simulator/src/public/utils.ts +++ b/yarn-project/simulator/src/public/utils.ts @@ -24,7 +24,8 @@ export function lastSideEffectCounter(tx: Tx): number { // look at both start and end counters because for enqueued public calls start > 0 while end === 0 max = Math.max(max, sideEffect.startSideEffectCounter.toNumber(), sideEffect.endSideEffectCounter.toNumber()); } else { - max = Math.max(max, sideEffect.counter.toNumber()); + const counter = typeof sideEffect.counter === 'number' ? sideEffect.counter : sideEffect.counter.toNumber(); + max = Math.max(max, counter); } }