From 1230e566698b62591a3bf8f35baa60c454f089e9 Mon Sep 17 00:00:00 2001 From: just-mitch <68168980+just-mitch@users.noreply.github.com> Date: Wed, 29 May 2024 10:44:11 +0200 Subject: [PATCH] chore: make public data update requests, note hashes, and unencrypted logs readonly in TS (#6658) Continue making fields in TS readonly. We do so by forcing side effect counters to be set in public, and then waiting until public kernel tail to: - recombine the non/revertible side effects and get hints (in TS) - provide the hints to the public kernel tail - using the hints in the public kernel tail while recombining to sort the public data update requests --- .../src/core/libraries/ConstantsGen.sol | 4 +- .../src/public_kernel_tail.nr | 65 +++++++-- .../src/public_kernel_teardown.nr | 11 +- .../src/reset/mutable_data_read_request.nr | 8 +- .../rollup-lib/src/base/base_rollup_inputs.nr | 5 +- .../crates/types/src/abis/accumulated_data.nr | 2 +- .../combined_accumulated_data.nr | 70 ++++++++-- .../types/src/abis/public_call_stack_item.nr | 4 +- .../src/abis/public_circuit_public_inputs.nr | 2 +- .../src/abis/public_data_update_request.nr | 19 ++- .../crates/types/src/constants.nr | 4 +- .../src/contrakt/storage_update_request.nr | 5 +- .../crates/types/src/tests/fixture_builder.nr | 2 +- .../src/tests/public_call_data_builder.nr | 3 +- yarn-project/circuits.js/src/constants.gen.ts | 4 +- .../src/hints/build_public_data_hints.test.ts | 5 +- ...ild_public_data_read_request_hints.test.ts | 4 +- .../public_call_stack_item.test.ts.snap | 8 +- .../public_circuit_public_inputs.test.ts.snap | 4 +- .../contract_storage_update_request.ts | 16 ++- yarn-project/circuits.js/src/structs/index.ts | 1 + .../src/structs/kernel/combine_hints.ts | 129 ++++++++++++++++++ .../kernel/combined_accumulated_data.ts | 36 +++-- .../structs/kernel/public_accumulated_data.ts | 9 +- ...blic_kernel_tail_circuit_private_inputs.ts | 4 + .../circuits.js/src/structs/log_hash.ts | 6 + .../src/structs/public_data_update_request.ts | 35 +++-- .../circuits.js/src/tests/factories.ts | 23 +++- .../composed/integration_l1_publisher.test.ts | 8 +- yarn-project/foundation/src/fields/fields.ts | 4 + .../src/type_conversion.ts | 23 ++++ .../prover-client/src/mocks/fixtures.ts | 4 +- .../build_private_kernel_tail_hints.ts | 2 +- .../src/public/abstract_phase_manager.ts | 25 +--- yarn-project/simulator/src/public/executor.ts | 4 +- .../src/public/public_processor.test.ts | 20 +-- .../simulator/src/public/public_processor.ts | 2 +- .../src/public/tail_phase_manager.ts | 52 ++----- .../simulator/src/public/utils.test.ts | 24 ++-- yarn-project/simulator/src/public/utils.ts | 34 +++-- 40 files changed, 490 insertions(+), 200 deletions(-) create mode 100644 yarn-project/circuits.js/src/structs/kernel/combine_hints.ts diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 7f4b29b5feb..8682d9a15a4 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -113,7 +113,7 @@ library Constants { uint256 internal constant CONTENT_COMMITMENT_LENGTH = 4; uint256 internal constant CONTRACT_INSTANCE_LENGTH = 5; uint256 internal constant CONTRACT_STORAGE_READ_LENGTH = 2; - uint256 internal constant CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 2; + uint256 internal constant CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3; uint256 internal constant ETH_ADDRESS_LENGTH = 1; uint256 internal constant FUNCTION_DATA_LENGTH = 2; uint256 internal constant FUNCTION_LEAF_PREIMAGE_LENGTH = 5; @@ -187,7 +187,7 @@ library Constants { + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX) + (SCOPED_KEY_VALIDATION_REQUEST_AND_GENERATOR_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX) + (PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX); - uint256 internal constant PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 2; + uint256 internal constant PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; uint256 internal constant COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NEW_NOTE_HASHES_PER_TX + MAX_NEW_NULLIFIERS_PER_TX + MAX_NEW_L2_TO_L1_MSGS_PER_TX + 6 + (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * PUBLIC_DATA_UPDATE_REQUEST_LENGTH) + GAS_LENGTH; diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index c37458ea749..4ac1172442d 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -5,13 +5,18 @@ use dep::reset_kernel_lib::{ }; use dep::types::{ abis::{ - accumulated_data::CombinedAccumulatedData, kernel_circuit_public_inputs::KernelCircuitPublicInputs, - public_kernel_data::PublicKernelData + accumulated_data::{CombinedAccumulatedData, CombineHints}, + kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData, + public_data_update_request::PublicDataUpdateRequest, side_effect::Ordered +}, + constants::{ + MAX_NEW_NOTE_HASHES_PER_TX, MAX_PUBLIC_DATA_HINTS, MAX_NULLIFIER_READ_REQUESTS_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }, - constants::{MAX_PUBLIC_DATA_HINTS, MAX_NULLIFIER_READ_REQUESTS_PER_TX}, data::public_data_hint::PublicDataHint, merkle_tree::{conditionally_assert_check_membership, MembershipWitness}, - partial_state_reference::PartialStateReference, utils::{arrays::array_length}, address::AztecAddress + partial_state_reference::PartialStateReference, + utils::{arrays::{array_length, assert_sorted_array}}, address::AztecAddress }; struct PublicKernelTailCircuitPrivateInputs { @@ -21,6 +26,7 @@ struct PublicKernelTailCircuitPrivateInputs { public_data_hints: [PublicDataHint; MAX_PUBLIC_DATA_HINTS], public_data_read_request_hints: PublicDataReadRequestHints, start_state: PartialStateReference, + combine_hints: CombineHints } impl PublicKernelTailCircuitPrivateInputs { @@ -46,10 +52,10 @@ impl PublicKernelTailCircuitPrivateInputs { fn propagate_accumulated_data(self) -> CombinedAccumulatedData { let previous_public_inputs = self.previous_kernel.public_inputs; - // TODO: Sort the combined data. CombinedAccumulatedData::combine( previous_public_inputs.end_non_revertible, - previous_public_inputs.end + previous_public_inputs.end, + self.combine_hints ) } @@ -98,7 +104,9 @@ mod tests { use dep::types::{ abis::{ kernel_circuit_public_inputs::KernelCircuitPublicInputs, public_kernel_data::PublicKernelData, - nullifier::ScopedNullifier, nullifier_leaf_preimage::NullifierLeafPreimage + nullifier::ScopedNullifier, nullifier_leaf_preimage::NullifierLeafPreimage, + accumulated_data::{CombinedAccumulatedData, CombineHints}, + public_data_update_request::PublicDataUpdateRequest, note_hash::NoteHash, log_hash::LogHash }, address::AztecAddress, constants::{ @@ -110,7 +118,7 @@ mod tests { }, hash::{silo_nullifier, sha256_to_field}, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, - tests::{fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree}, + tests::{fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree, sort::sort_get_sorted_hints}, traits::is_empty, partial_state_reference::PartialStateReference, utils::arrays::array_merge, merkle_tree::MembershipWitness }; @@ -292,13 +300,52 @@ mod tests { let mut previous_kernel = self.previous_kernel.to_public_kernel_data(false); previous_kernel.public_inputs.end = self.previous_revertible.to_public_accumulated_data(); + // Note: note hashes are a bit odd here: whereas we'd like to use `combined` and then + // sort the result, we cannot because we lose the side effect counters when we combine. + let merged = array_merge( + previous_kernel.public_inputs.end_non_revertible.new_note_hashes, + previous_kernel.public_inputs.end.new_note_hashes + ); + let sorted = sort_get_sorted_hints(merged, |a: NoteHash, b: NoteHash| a.counter() < b.counter()); + let sorted_note_hashes = sorted.sorted_array; + let sorted_note_hashes_indexes = sorted.sorted_index_hints; + + let merged = array_merge( + previous_kernel.public_inputs.end_non_revertible.unencrypted_logs_hashes, + previous_kernel.public_inputs.end.unencrypted_logs_hashes + ); + let sorted = sort_get_sorted_hints(merged, |a: LogHash, b: LogHash| a.counter() < b.counter()); + let sorted_unencrypted_logs_hashes = sorted.sorted_array; + let sorted_unencrypted_logs_hashes_indexes = sorted.sorted_index_hints; + + let merged = array_merge( + previous_kernel.public_inputs.end_non_revertible.public_data_update_requests, + previous_kernel.public_inputs.end.public_data_update_requests + ); + let sorted = sort_get_sorted_hints( + merged, + |a: PublicDataUpdateRequest, b: PublicDataUpdateRequest| a.counter() < b.counter() + ); + let sorted_public_data_update_requests = sorted.sorted_array; + let sorted_public_data_update_requests_indexes = sorted.sorted_index_hints; + + let combine_hints = CombineHints { + sorted_note_hashes, + sorted_note_hashes_indexes, + sorted_unencrypted_logs_hashes, + sorted_unencrypted_logs_hashes_indexes, + sorted_public_data_update_requests, + sorted_public_data_update_requests_indexes + }; + let kernel = PublicKernelTailCircuitPrivateInputs { previous_kernel, nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), nullifier_non_existent_read_request_hints: self.nullifier_non_existent_read_request_hints_builder.to_hints(), public_data_hints: self.public_data_hints.storage, public_data_read_request_hints: self.public_data_read_request_hints_builder.to_hints(), - start_state: self.start_state + start_state: self.start_state, + combine_hints }; kernel.public_kernel_tail() diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr index 951c70283ba..736b3f79047 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr @@ -548,23 +548,32 @@ mod tests { } #[test] - unconstrained fn correctly_updates_revert_code() { + unconstrained fn correctly_updates_revert_code_0() { let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); let public_inputs = builder.execute(); assert_eq(public_inputs.revert_code, 0); + } + #[test] + unconstrained fn correctly_updates_revert_code_1() { // Case where we carry forward a revert code from app logic let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); builder.previous_kernel.revert_code = 1; let public_inputs = builder.execute(); assert_eq(public_inputs.revert_code, 1); + } + #[test] + unconstrained fn correctly_updates_revert_code_2() { // Case where there is a new error in teardown let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); builder.public_call.public_inputs.revert_code = 1; let public_inputs = builder.execute(); assert_eq(public_inputs.revert_code, 2); + } + #[test] + unconstrained fn correctly_updates_revert_code_3() { // Case where there is an error in both app logic and teardown let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); builder.previous_kernel.revert_code = 1; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr index 299970a6f13..f73536b4f1c 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr @@ -138,10 +138,10 @@ mod tests { } global data_writes = [ - PublicDataUpdateRequest { leaf_slot: 22, new_value: 200 }, - PublicDataUpdateRequest { leaf_slot: 11, new_value: 100 }, - PublicDataUpdateRequest { leaf_slot: 33, new_value: 300 }, - PublicDataUpdateRequest { leaf_slot: 44, new_value: 400 } + PublicDataUpdateRequest { leaf_slot: 22, new_value: 200, counter: 0 }, + PublicDataUpdateRequest { leaf_slot: 11, new_value: 100, counter: 1 }, + PublicDataUpdateRequest { leaf_slot: 33, new_value: 300, counter: 2 }, + PublicDataUpdateRequest { leaf_slot: 44, new_value: 400, counter: 3 } ]; global leaf_data_hints = [ 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 8cb43e345a3..04514890c11 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 @@ -273,7 +273,7 @@ impl BaseRollupInputs { ); let new_value = compute_public_data_tree_value(existing_update.new_value - tx_fee); - let protocol_update_request = PublicDataUpdateRequest { leaf_slot, new_value }; + let protocol_update_request = PublicDataUpdateRequest { leaf_slot, new_value, counter: 0 }; (protocol_update_request, existing_update_index as u64) } else { // Otherwise, build a new one to be inserted into the protocol update requests at the end of the array. @@ -284,7 +284,7 @@ impl BaseRollupInputs { assert(!balance.lt(tx_fee), "Not enough balance for fee payer to pay for transaction"); let new_value = compute_public_data_tree_value(balance - tx_fee); - let protocol_update_request = PublicDataUpdateRequest { leaf_slot, new_value }; + let protocol_update_request = PublicDataUpdateRequest { leaf_slot, new_value, counter: 0 }; (protocol_update_request, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) } } else { @@ -505,6 +505,7 @@ mod tests { kernel_data.public_inputs.end.public_data_update_requests[i] = PublicDataUpdateRequest { leaf_slot : leaf.slot, new_value : leaf.value, + counter : 0 }; } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data.nr index 2e8ec3cef79..7159c14a363 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data.nr @@ -5,7 +5,7 @@ mod public_accumulated_data; mod public_accumulated_data_builder; use crate::abis::accumulated_data::{ - combined_accumulated_data::CombinedAccumulatedData, + combined_accumulated_data::CombinedAccumulatedData, combined_accumulated_data::CombineHints, private_accumulated_data::PrivateAccumulatedData, private_accumulated_data_builder::PrivateAccumulatedDataBuilder, public_accumulated_data::PublicAccumulatedData, 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 c8c869e53ce..fb987377fdf 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 @@ -3,15 +3,26 @@ use crate::{ abis::{ accumulated_data::public_accumulated_data::PublicAccumulatedData, note_hash::NoteHash, nullifier::Nullifier, public_data_update_request::PublicDataUpdateRequest, - log_hash::{LogHash, NoteLogHash}, gas::Gas + log_hash::{LogHash, NoteLogHash}, gas::Gas, side_effect::Ordered }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, COMBINED_ACCUMULATED_DATA_LENGTH + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, COMBINED_ACCUMULATED_DATA_LENGTH, + MAX_UNENCRYPTED_LOGS_PER_TX }, - utils::{arrays::array_merge, reader::Reader}, traits::{Empty, Serialize, Deserialize} + utils::{arrays::{array_merge, assert_sorted_array}, reader::Reader}, + traits::{Empty, Serialize, Deserialize} }; +struct CombineHints { + sorted_note_hashes: [NoteHash; MAX_NEW_NOTE_HASHES_PER_TX], + sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + sorted_unencrypted_logs_hashes: [LogHash; MAX_UNENCRYPTED_LOGS_PER_TX], + sorted_unencrypted_logs_hashes_indexes: [u64; MAX_UNENCRYPTED_LOGS_PER_TX], + sorted_public_data_update_requests: [PublicDataUpdateRequest; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + sorted_public_data_update_requests_indexes: [u64; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], +} + struct CombinedAccumulatedData { new_note_hashes: [Field; MAX_NEW_NOTE_HASHES_PER_TX], new_nullifiers: [Field; MAX_NEW_NULLIFIERS_PER_TX], @@ -32,8 +43,35 @@ struct CombinedAccumulatedData { gas_used: Gas, } +fn asc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { + a.counter() <= b.counter() +} + impl CombinedAccumulatedData { - pub fn combine(non_revertible: PublicAccumulatedData, revertible: PublicAccumulatedData) -> Self { + pub fn combine( + non_revertible: PublicAccumulatedData, + revertible: PublicAccumulatedData, + combine_hints: CombineHints + ) -> Self { + let merged_note_hashes = array_merge(non_revertible.new_note_hashes, revertible.new_note_hashes); + assert_sorted_array( + merged_note_hashes, + combine_hints.sorted_note_hashes, + combine_hints.sorted_note_hashes_indexes, + asc_sort_by_counters + ); + + let merged_public_data_update_requests = array_merge( + non_revertible.public_data_update_requests, + revertible.public_data_update_requests + ); + assert_sorted_array( + merged_public_data_update_requests, + combine_hints.sorted_public_data_update_requests, + combine_hints.sorted_public_data_update_requests_indexes, + asc_sort_by_counters + ); + // TODO(Miranda): Hash here or elsewhere? let note_encrypted_logs_hash = compute_tx_note_logs_hash( array_merge( @@ -47,12 +85,19 @@ impl CombinedAccumulatedData { revertible.encrypted_logs_hashes ) ); - let unencrypted_logs_hash = compute_tx_logs_hash( - array_merge( - non_revertible.unencrypted_logs_hashes, - revertible.unencrypted_logs_hashes - ) + + let merged_unencrypted_logs_hashes = array_merge( + non_revertible.unencrypted_logs_hashes, + revertible.unencrypted_logs_hashes ); + assert_sorted_array( + merged_unencrypted_logs_hashes, + combine_hints.sorted_unencrypted_logs_hashes, + combine_hints.sorted_unencrypted_logs_hashes_indexes, + asc_sort_by_counters + ); + let unencrypted_logs_hash = compute_tx_logs_hash(combine_hints.sorted_unencrypted_logs_hashes); + let note_encrypted_log_preimages_length = non_revertible.note_encrypted_logs_hashes.fold(0, |a, b: LogHash| a + b.length) + revertible.note_encrypted_logs_hashes.fold(0, |a, b: LogHash| a + b.length); let encrypted_log_preimages_length = non_revertible.encrypted_logs_hashes.fold(0, |a, b: LogHash| a + b.length) @@ -60,7 +105,7 @@ impl CombinedAccumulatedData { let unencrypted_log_preimages_length = non_revertible.unencrypted_logs_hashes.fold(0, |a, b: LogHash| a + b.length) + revertible.unencrypted_logs_hashes.fold(0, |a, b: LogHash| a + b.length); CombinedAccumulatedData { - new_note_hashes: array_merge(non_revertible.new_note_hashes, revertible.new_note_hashes).map(|n: NoteHash| n.value), + new_note_hashes: combine_hints.sorted_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: array_merge( non_revertible.new_l2_to_l1_msgs, @@ -72,10 +117,7 @@ impl CombinedAccumulatedData { note_encrypted_log_preimages_length, encrypted_log_preimages_length, unencrypted_log_preimages_length, - public_data_update_requests: array_merge( - non_revertible.public_data_update_requests, - revertible.public_data_update_requests - ), + public_data_update_requests: combine_hints.sorted_public_data_update_requests, gas_used: revertible.gas_used + non_revertible.gas_used } } 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 6746118086d..40b6243801c 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 @@ -69,7 +69,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: true, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item request hash" test - let test_data_call_stack_item_request_hash = 0x11998b1d33b8ba1c8fa7a6c2f5bc76b31bbaa80400554465c335ba31559ac1f9; + let test_data_call_stack_item_request_hash = 0x09b460df8be10a6bd56588c93b478243fdf5cc92db59d9b1670ce2a044fab6d6; assert_eq(call_stack_item.hash(), test_data_call_stack_item_request_hash); } @@ -87,7 +87,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: false, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item hash" test - let test_data_call_stack_item_hash = 0x2b7f8b68d96d0011ecc576459899e9451fbd880568ccc7a071d9cf04e59abb65; + let test_data_call_stack_item_hash = 0x0931a8de516f3f49dff48fbdea57f01b706dc67cbd1fd8bde97d26c4a53afd0a; assert_eq(call_stack_item.hash(), test_data_call_stack_item_hash); } } 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 157171f2648..3b9589d9ed2 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 @@ -188,6 +188,6 @@ fn empty_hash() { let hash = inputs.hash(); // Value from public_circuit_public_inputs.test.ts "computes empty item hash" test - let test_data_empty_hash = 0x1e4351db0c9aa20836e7009bc3e6a4555c92622c5e9cb3b49e2ec0fbbf59d0bd; + let test_data_empty_hash = 0x05061edabeae1057b6f6216afc118e46ea0b77044d4c57ed12e5712dab01b8d4; assert_eq(hash, test_data_empty_hash); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr index 5543269aaf3..09779b0d812 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_update_request.nr @@ -5,10 +5,12 @@ use crate::public_data_tree_leaf::PublicDataTreeLeaf; use crate::address::AztecAddress; use crate::contrakt::storage_update_request::StorageUpdateRequest; use crate::data::hash::{compute_public_data_tree_value, compute_public_data_tree_index}; +use crate::abis::side_effect::Ordered; struct PublicDataUpdateRequest { leaf_slot : Field, - new_value : Field + new_value : Field, + counter: u32 } impl PublicDataUpdateRequest { @@ -18,11 +20,18 @@ impl PublicDataUpdateRequest { ) -> PublicDataUpdateRequest { PublicDataUpdateRequest { leaf_slot: compute_public_data_tree_index(contract_address, update_request.storage_slot), - new_value: compute_public_data_tree_value(update_request.new_value) + new_value: compute_public_data_tree_value(update_request.new_value), + counter: update_request.counter } } } +impl Ordered for PublicDataUpdateRequest { + fn counter(self)-> u32{ + self.counter + } +} + impl Eq for PublicDataUpdateRequest { fn eq(self, update_request: PublicDataUpdateRequest) -> bool { (update_request.leaf_slot == self.leaf_slot) @@ -34,7 +43,8 @@ impl Empty for PublicDataUpdateRequest { fn empty() -> Self { Self { leaf_slot : 0, - new_value : 0 + new_value : 0, + counter : 0 } } } @@ -66,7 +76,7 @@ impl PublicDataUpdateRequest { impl Serialize for PublicDataUpdateRequest { fn serialize(self) -> [Field; PUBLIC_DATA_UPDATE_REQUEST_LENGTH] { - [self.leaf_slot, self.new_value] + [self.leaf_slot, self.new_value, self.counter as Field] } } @@ -75,6 +85,7 @@ impl Deserialize for PublicDataUpdateRequest PublicDataUpdateRequest { leaf_slot: fields[0], new_value: fields[1], + counter: fields[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 4298295827f..4a51e9a2300 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -154,7 +154,7 @@ global CALL_CONTEXT_LENGTH: u64 = 6; global CONTENT_COMMITMENT_LENGTH: u64 = 4; global CONTRACT_INSTANCE_LENGTH: u64 = 5; global CONTRACT_STORAGE_READ_LENGTH: u64 = 2; -global CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH: u64 = 2; +global CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH: u64 = 3; global ETH_ADDRESS_LENGTH = 1; global FUNCTION_DATA_LENGTH: u64 = 2; global FUNCTION_LEAF_PREIMAGE_LENGTH: u64 = 5; @@ -199,7 +199,7 @@ global SCOPED_READ_REQUEST_LEN = READ_REQUEST_LENGTH + 1; global PUBLIC_DATA_READ_LENGTH = 2; global VALIDATION_REQUESTS_LENGTH = ROLLUP_VALIDATION_REQUESTS_LENGTH + (SCOPED_READ_REQUEST_LEN * MAX_NOTE_HASH_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX) + (SCOPED_KEY_VALIDATION_REQUEST_AND_GENERATOR_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX) + (PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX); -global PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 2; +global PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; global COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NEW_NOTE_HASHES_PER_TX + MAX_NEW_NULLIFIERS_PER_TX + MAX_NEW_L2_TO_L1_MSGS_PER_TX + 6 + (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * PUBLIC_DATA_UPDATE_REQUEST_LENGTH) + GAS_LENGTH; global COMBINED_CONSTANT_DATA_LENGTH = HEADER_LENGTH + TX_CONTEXT_LENGTH + GLOBAL_VARIABLES_LENGTH; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr index 5c9e244f937..01f0acf1eb1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contrakt/storage_update_request.nr @@ -7,6 +7,7 @@ use dep::std::cmp::Eq; struct StorageUpdateRequest { storage_slot : Field, new_value : Field, + counter: u32 } impl Eq for StorageUpdateRequest { @@ -21,6 +22,7 @@ impl Empty for StorageUpdateRequest { StorageUpdateRequest { storage_slot: 0, new_value: 0, + counter: 0 } } } @@ -33,7 +35,7 @@ impl Hash for StorageUpdateRequest { impl Serialize for StorageUpdateRequest { fn serialize(self) -> [Field; CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH] { - [self.storage_slot, self.new_value] + [self.storage_slot, self.new_value, self.counter as Field] } } @@ -42,6 +44,7 @@ impl Deserialize for StorageUpdateReques StorageUpdateRequest { storage_slot: serialized[0], new_value: serialized[1], + counter: serialized[2] as u32 } } } 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 489df58716d..2f8d5516e12 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 @@ -344,7 +344,7 @@ impl FixtureBuilder { } pub fn add_public_data_update_request(&mut self, leaf_slot: Field, value: Field) { - let update_request = PublicDataUpdateRequest { leaf_slot, new_value: value }; + let update_request = PublicDataUpdateRequest { leaf_slot, new_value: value, counter: self.next_counter() }; self.public_data_update_requests.push(update_request); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr index 258c70f3145..ed3f4825ec6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr @@ -128,7 +128,8 @@ impl PublicCallDataBuilder { // The default storage slot is its index + 1. storage_slot: (value_offset + i + 1) as Field, // The default value is its index + 890. - new_value: (value_offset + i + 890) as Field + new_value: (value_offset + i + 890) as Field, + counter: i as u32 }; self.public_inputs.contract_storage_update_requests.push(update_request); } diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 7bbdd32cf05..3a11892a0a6 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -95,7 +95,7 @@ export const CALL_CONTEXT_LENGTH = 6; export const CONTENT_COMMITMENT_LENGTH = 4; export const CONTRACT_INSTANCE_LENGTH = 5; export const CONTRACT_STORAGE_READ_LENGTH = 2; -export const CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 2; +export const CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3; export const ETH_ADDRESS_LENGTH = 1; export const FUNCTION_DATA_LENGTH = 2; export const FUNCTION_LEAF_PREIMAGE_LENGTH = 5; @@ -186,7 +186,7 @@ export const VALIDATION_REQUESTS_LENGTH = SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX + SCOPED_KEY_VALIDATION_REQUEST_AND_GENERATOR_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX + PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX; -export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 2; +export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; export const COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NEW_NOTE_HASHES_PER_TX + MAX_NEW_NULLIFIERS_PER_TX + diff --git a/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts b/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts index 9c16f9c77d9..3e038e13c9d 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_hints.test.ts @@ -30,6 +30,9 @@ describe('buildPublicDataHints', () => { let publicDataReads: Tuple; let publicDataUpdateRequests: Tuple; let expectedHints: Tuple; + let sideEffectCounter = 0; + + const nextSideEffectCounter = () => sideEffectCounter++; const publicDataLeaves = [ new PublicDataTreeLeafPreimage(new Fr(22), new Fr(200), new Fr(33), 0n), @@ -39,7 +42,7 @@ describe('buildPublicDataHints', () => { const makePublicDataRead = (leafSlot: number, value: number) => new PublicDataRead(new Fr(leafSlot), new Fr(value)); const makePublicDataWrite = (leafSlot: number, value: number) => - new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value)); + new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value), nextSideEffectCounter()); const oracle = { getMatchOrLowPublicDataMembershipWitness: (leafSlot: bigint) => { diff --git a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts b/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts index 164256ede3e..4afea4a13b9 100644 --- a/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_public_data_read_request_hints.test.ts @@ -23,9 +23,11 @@ describe('buildPublicDataReadRequestHints', () => { let expectedStatuses: Tuple; let expectedPendingHints: Tuple; let expectedLeafDataHints: Tuple; + let counter = 0; + const nextCounter = () => counter++; const makePublicDataWrite = (leafSlot: number, value: number) => - new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value)); + new PublicDataUpdateRequest(new Fr(leafSlot), new Fr(value), nextCounter()); const makePublicDataHint = (slot: number, value: number) => { const hint = PublicDataHint.empty(); hint.leafSlot = new Fr(slot); 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 380fc1ae9c6..70445295be3 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 @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x2b7f8b68d96d0011ecc576459899e9451fbd880568ccc7a071d9cf04e59abb65"`; +exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x0931a8de516f3f49dff48fbdea57f01b706dc67cbd1fd8bde97d26c4a53afd0a"`; -exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x11998b1d33b8ba1c8fa7a6c2f5bc76b31bbaa80400554465c335ba31559ac1f9"`; +exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x09b460df8be10a6bd56588c93b478243fdf5cc92db59d9b1670ce2a044fab6d6"`; -exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x2e7cef26b4ef88a036f6f2bc5bd5d7457b7c2851c7357f1e6f79be9fdde4cf77>`; +exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x2baaf9d674eb8f64e18bca2f87a665fdbd43d8696c7a62d689d88bd0296175a4>`; -exports[`PublicCallStackItem computes hash 1`] = `Fr<0x1506520f17c103197070a8f5dec9c0665fefd4251d93ee52f32c07aad554fc78>`; +exports[`PublicCallStackItem computes hash 1`] = `Fr<0x17703e5a5555de5e97d3e6c991b489f4818e71931fd8ee958a00e2c18b806072>`; 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 e666e2b7cbf..635a3f60030 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 @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PublicCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x1e4351db0c9aa20836e7009bc3e6a4555c92622c5e9cb3b49e2ec0fbbf59d0bd>`; +exports[`PublicCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x05061edabeae1057b6f6216afc118e46ea0b77044d4c57ed12e5712dab01b8d4>`; -exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x099d3f1f21b3405cf1dffefd8dd045f4a628ecdd2a717ac7a41e4cc3d41ff79b>`; +exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x14d9838268c0549fdbc4231fc36432f7ec6687157287117bf15b2fb5a39c9026>`; diff --git a/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts b/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts index 378279d55bb..04be2dd24f9 100644 --- a/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts +++ b/yarn-project/circuits.js/src/structs/contract_storage_update_request.ts @@ -24,17 +24,17 @@ export class ContractStorageUpdateRequest { /** * Optional side effect counter tracking position of this event in tx execution. */ - public readonly sideEffectCounter?: number, + public readonly sideEffectCounter: number, public contractAddress?: AztecAddress, // TODO: Should not be optional. This is a temporary hack to silo the storage slot with the correct address for nested executions. ) {} toBuffer() { - return serializeToBuffer(this.storageSlot, this.newValue); + return serializeToBuffer(this.storageSlot, this.newValue, this.sideEffectCounter); } static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); - return new ContractStorageUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader)); + return new ContractStorageUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader), reader.readNumber()); } /** @@ -56,7 +56,7 @@ export class ContractStorageUpdateRequest { } static empty() { - return new ContractStorageUpdateRequest(Fr.ZERO, Fr.ZERO); + return new ContractStorageUpdateRequest(Fr.ZERO, Fr.ZERO, 0); } isEmpty() { @@ -64,11 +64,13 @@ export class ContractStorageUpdateRequest { } toFriendlyJSON() { - return `Slot=${this.storageSlot.toFriendlyJSON()}: ${this.newValue.toFriendlyJSON()}`; + return `Slot=${this.storageSlot.toFriendlyJSON()}: ${this.newValue.toFriendlyJSON()}, sideEffectCounter=${ + this.sideEffectCounter + }`; } toFields(): Fr[] { - const fields = [this.storageSlot, this.newValue]; + const fields = [this.storageSlot, this.newValue, new Fr(this.sideEffectCounter)]; if (fields.length !== CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH) { throw new Error( `Invalid number of fields for ContractStorageUpdateRequest. Expected ${CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH}, got ${fields.length}`, @@ -83,6 +85,6 @@ export class ContractStorageUpdateRequest { const storageSlot = reader.readField(); const newValue = reader.readField(); - return new ContractStorageUpdateRequest(storageSlot, newValue); + return new ContractStorageUpdateRequest(storageSlot, newValue, reader.readU32()); } } diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 96ca0032582..d30c2454a87 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -14,6 +14,7 @@ export * from './gas_fees.js'; export * from './gas_settings.js'; export * from './global_variables.js'; export * from './header.js'; +export * from './kernel/combine_hints.js'; export * from './kernel/combined_accumulated_data.js'; export * from './kernel/combined_constant_data.js'; export * from './kernel/kernel_circuit_public_inputs.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/combine_hints.ts b/yarn-project/circuits.js/src/structs/kernel/combine_hints.ts new file mode 100644 index 00000000000..6b4954f587d --- /dev/null +++ b/yarn-project/circuits.js/src/structs/kernel/combine_hints.ts @@ -0,0 +1,129 @@ +import { type FieldsOf } from '@aztec/foundation/array'; +import { removeArrayPaddingEnd } from '@aztec/foundation/collection'; +import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { inspect } from 'util'; + +import { + MAX_ENCRYPTED_LOGS_PER_TX, + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX, +} from '../../constants.gen.js'; +import { getNonEmptyItems, mergeAccumulatedData, sortByCounterGetSortedHints } from '../../utils/index.js'; +import { LogHash } from '../log_hash.js'; +import { NoteHash } from '../note_hash.js'; +import { PublicDataUpdateRequest } from '../public_data_update_request.js'; +import { type PublicAccumulatedData } from './public_accumulated_data.js'; + +export class CombineHints { + constructor( + public readonly sortedNoteHashes: Tuple, + public readonly sortedNoteHashesIndexes: Tuple, + public readonly sortedUnencryptedLogsHashes: Tuple, + public readonly sortedUnencryptedLogsHashesIndexes: Tuple, + public readonly sortedPublicDataUpdateRequests: Tuple< + PublicDataUpdateRequest, + typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + >, + public readonly sortedPublicDataUpdateRequestsIndexes: Tuple, + ) {} + + static getFields(fields: FieldsOf) { + return [ + fields.sortedNoteHashes, + fields.sortedNoteHashesIndexes, + fields.sortedUnencryptedLogsHashes, + fields.sortedUnencryptedLogsHashesIndexes, + fields.sortedPublicDataUpdateRequests, + fields.sortedPublicDataUpdateRequestsIndexes, + ] as const; + } + + static from(fields: FieldsOf): CombineHints { + return new CombineHints(...CombineHints.getFields(fields)); + } + + toBuffer() { + return serializeToBuffer(...CombineHints.getFields(this)); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new CombineHints( + reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, NoteHash), + reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), + reader.readArray(MAX_UNENCRYPTED_LOGS_PER_TX, LogHash), + reader.readNumbers(MAX_UNENCRYPTED_LOGS_PER_TX), + reader.readArray(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataUpdateRequest), + reader.readNumbers(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX), + ); + } + + static fromPublicData({ + revertibleData, + nonRevertibleData, + }: { + revertibleData: PublicAccumulatedData; + nonRevertibleData: PublicAccumulatedData; + }): CombineHints { + const mergedNoteHashes = mergeAccumulatedData( + nonRevertibleData.newNoteHashes, + revertibleData.newNoteHashes, + MAX_NEW_NOTE_HASHES_PER_TX, + ); + + const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints( + mergedNoteHashes, + MAX_NEW_NOTE_HASHES_PER_TX, + ); + + const unencryptedLogHashes = mergeAccumulatedData( + nonRevertibleData.unencryptedLogsHashes, + revertibleData.unencryptedLogsHashes, + MAX_ENCRYPTED_LOGS_PER_TX, + ); + + const [sortedUnencryptedLogsHashes, sortedUnencryptedLogsHashesIndexes] = sortByCounterGetSortedHints( + unencryptedLogHashes, + MAX_ENCRYPTED_LOGS_PER_TX, + ); + + const publicDataUpdateRequests = mergeAccumulatedData( + nonRevertibleData.publicDataUpdateRequests, + revertibleData.publicDataUpdateRequests, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ); + + const [sortedPublicDataUpdateRequests, sortedPublicDataUpdateRequestsIndexes] = sortByCounterGetSortedHints( + publicDataUpdateRequests, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ); + + return CombineHints.from({ + sortedNoteHashes, + sortedNoteHashesIndexes, + sortedUnencryptedLogsHashes, + sortedUnencryptedLogsHashesIndexes, + sortedPublicDataUpdateRequests, + sortedPublicDataUpdateRequestsIndexes, + }); + } + + [inspect.custom](): string { + return `CombineHints { + sortedNoteHashes: ${getNonEmptyItems(this.sortedNoteHashes) + .map(h => inspect(h)) + .join(', ')}, + sortedNoteHashesIndexes: ${removeArrayPaddingEnd(this.sortedNoteHashesIndexes, n => n === 0)}, + sortedUnencryptedLogsHashes: ${getNonEmptyItems(this.sortedUnencryptedLogsHashes) + .map(h => inspect(h)) + .join(', ')}, + sortedUnencryptedLogsHashesIndexes: ${removeArrayPaddingEnd(this.sortedUnencryptedLogsHashesIndexes, n => n === 0)}, + sortedPublicDataUpdateRequests: ${getNonEmptyItems(this.sortedPublicDataUpdateRequests) + .map(h => inspect(h)) + .join(', ')}, + sortedPublicDataUpdateRequestsIndexes: ${this.sortedPublicDataUpdateRequestsIndexes} +}`; + } +} diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index 302e0887a54..035c1999988 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -1,4 +1,4 @@ -import { makeTuple } from '@aztec/foundation/array'; +import { type FieldsOf, makeTuple } from '@aztec/foundation/array'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -67,20 +67,28 @@ export class CombinedAccumulatedData { public gasUsed: Gas, ) {} + static getFields(fields: FieldsOf) { + return [ + fields.newNoteHashes, + fields.newNullifiers, + fields.newL2ToL1Msgs, + fields.noteEncryptedLogsHash, + fields.encryptedLogsHash, + fields.unencryptedLogsHash, + fields.noteEncryptedLogPreimagesLength, + fields.encryptedLogPreimagesLength, + fields.unencryptedLogPreimagesLength, + fields.publicDataUpdateRequests, + fields.gasUsed, + ] as const; + } + + static from(fields: FieldsOf): CombinedAccumulatedData { + return new CombinedAccumulatedData(...CombinedAccumulatedData.getFields(fields)); + } + toBuffer() { - return serializeToBuffer( - this.newNoteHashes, - this.newNullifiers, - this.newL2ToL1Msgs, - this.noteEncryptedLogsHash, - this.encryptedLogsHash, - this.unencryptedLogsHash, - this.noteEncryptedLogPreimagesLength, - this.encryptedLogPreimagesLength, - this.unencryptedLogPreimagesLength, - this.publicDataUpdateRequests, - this.gasUsed, - ); + return serializeToBuffer(...CombinedAccumulatedData.getFields(this)); } toString() { 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 0bdf2481f70..eb25838af82 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 @@ -27,7 +27,7 @@ export class PublicAccumulatedData { /** * The new note hashes made in this transaction. */ - public newNoteHashes: Tuple, + public readonly newNoteHashes: Tuple, /** * The new nullifiers made in this transaction. */ @@ -50,11 +50,14 @@ export class PublicAccumulatedData { * Accumulated unencrypted logs hashes from all the previous kernel iterations. * Note: Truncated to 31 bytes to fit in Fr. */ - public unencryptedLogsHashes: Tuple, + public readonly unencryptedLogsHashes: Tuple, /** * All the public data update requests made in this transaction. */ - public publicDataUpdateRequests: Tuple, + public readonly publicDataUpdateRequests: Tuple< + PublicDataUpdateRequest, + typeof MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + >, /** * Current public call stack. */ diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts index c6f22d4b536..639296898f1 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts @@ -9,6 +9,7 @@ import { PartialStateReference } from '../partial_state_reference.js'; import { PublicDataHint } from '../public_data_hint.js'; import { PublicDataReadRequestHints } from '../public_data_read_request_hints.js'; import { type NullifierReadRequestHints, nullifierReadRequestHintsFromBuffer } from '../read_request_hints/index.js'; +import { CombineHints } from './combine_hints.js'; import { PublicKernelData } from './public_kernel_data.js'; export class PublicKernelTailCircuitPrivateInputs { @@ -31,6 +32,7 @@ export class PublicKernelTailCircuitPrivateInputs { public readonly publicDataHints: Tuple, public readonly publicDataReadRequestHints: PublicDataReadRequestHints, public readonly startState: PartialStateReference, + public readonly combineHints: CombineHints, ) {} toBuffer() { @@ -41,6 +43,7 @@ export class PublicKernelTailCircuitPrivateInputs { this.publicDataHints, this.publicDataReadRequestHints, this.startState, + this.combineHints, ); } @@ -65,6 +68,7 @@ export class PublicKernelTailCircuitPrivateInputs { reader.readArray(MAX_PUBLIC_DATA_HINTS, PublicDataHint), reader.readObject(PublicDataReadRequestHints), reader.readObject(PartialStateReference), + reader.readObject(CombineHints), ); } diff --git a/yarn-project/circuits.js/src/structs/log_hash.ts b/yarn-project/circuits.js/src/structs/log_hash.ts index 7f845e69485..86105784e5c 100644 --- a/yarn-project/circuits.js/src/structs/log_hash.ts +++ b/yarn-project/circuits.js/src/structs/log_hash.ts @@ -2,6 +2,8 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { inspect } from 'util'; + import { type Ordered } from '../interfaces/index.js'; export class LogHash implements Ordered { @@ -36,6 +38,10 @@ export class LogHash implements Ordered { toString(): string { return `value=${this.value} counter=${this.counter} length=${this.length}`; } + + [inspect.custom](): string { + return `LogHash { ${this.toString()} }`; + } } export class ScopedLogHash implements Ordered { diff --git a/yarn-project/circuits.js/src/structs/public_data_update_request.ts b/yarn-project/circuits.js/src/structs/public_data_update_request.ts index c34bf44a58e..a6b5d99a896 100644 --- a/yarn-project/circuits.js/src/structs/public_data_update_request.ts +++ b/yarn-project/circuits.js/src/structs/public_data_update_request.ts @@ -1,6 +1,8 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { inspect } from 'util'; + /** * Write operations on the public data tree including the previous value. */ @@ -15,9 +17,9 @@ export class PublicDataUpdateRequest { */ public readonly newValue: Fr, /** - * Optional side effect counter tracking position of this event in tx execution. + * Side effect counter tracking position of this event in tx execution. */ - public readonly sideEffectCounter?: number, + public readonly sideEffectCounter: number, ) {} static from(args: { @@ -29,12 +31,21 @@ export class PublicDataUpdateRequest { * New value of the leaf. */ newValue: Fr; + + /** + * Side effect counter tracking position of this event in tx execution. + */ + sideEffectCounter: number; }) { - return new PublicDataUpdateRequest(args.leafIndex, args.newValue); + return new PublicDataUpdateRequest(args.leafIndex, args.newValue, args.sideEffectCounter); + } + + get counter() { + return this.sideEffectCounter; } toBuffer() { - return serializeToBuffer(this.leafSlot, this.newValue); + return serializeToBuffer(this.leafSlot, this.newValue, this.sideEffectCounter); } isEmpty() { @@ -43,7 +54,7 @@ export class PublicDataUpdateRequest { static fromFields(fields: Fr[] | FieldReader) { const reader = FieldReader.asReader(fields); - return new PublicDataUpdateRequest(reader.readField(), reader.readField()); + return new PublicDataUpdateRequest(reader.readField(), reader.readField(), reader.readU32()); } static isEmpty(x: PublicDataUpdateRequest) { @@ -56,14 +67,22 @@ export class PublicDataUpdateRequest { static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); - return new PublicDataUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader)); + return new PublicDataUpdateRequest(Fr.fromBuffer(reader), Fr.fromBuffer(reader), reader.readNumber()); } static empty() { - return new PublicDataUpdateRequest(Fr.ZERO, Fr.ZERO); + return new PublicDataUpdateRequest(Fr.ZERO, Fr.ZERO, 0); } toFriendlyJSON() { - return `Leaf=${this.leafSlot.toFriendlyJSON()}: ${this.newValue.toFriendlyJSON()}`; + return `Leaf=${this.leafSlot.toFriendlyJSON()}: ${this.newValue.toFriendlyJSON()}, SideEffectCounter=${ + this.sideEffectCounter + }`; + } + + [inspect.custom]() { + return `PublicDataUpdateRequest { leafSlot: ${this.leafSlot.toFriendlyJSON()}, newValue: ${this.newValue.toFriendlyJSON()}, sideEffectCounter: ${ + this.sideEffectCounter + } }`; } } diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index cd429991dfe..4f250c417ad 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -21,6 +21,7 @@ import { CallContext, CallRequest, CallerContext, + CombineHints, CombinedAccumulatedData, CombinedConstantData, ConstantRollupData, @@ -249,7 +250,7 @@ function makeScopedKeyValidationRequestAndGenerators(seed: number): ScopedKeyVal * @returns A public data update request. */ export function makePublicDataUpdateRequest(seed = 1): PublicDataUpdateRequest { - return new PublicDataUpdateRequest(fr(seed), fr(seed + 1)); + return new PublicDataUpdateRequest(fr(seed), fr(seed + 1), seed + 2); } /** @@ -257,7 +258,7 @@ export function makePublicDataUpdateRequest(seed = 1): PublicDataUpdateRequest { * @returns An empty public data update request. */ export function makeEmptyPublicDataUpdateRequest(): PublicDataUpdateRequest { - return new PublicDataUpdateRequest(fr(0), fr(0)); + return new PublicDataUpdateRequest(fr(0), fr(0), 0); } /** @@ -283,7 +284,7 @@ export function makeEmptyPublicDataRead(): PublicDataRead { * @returns A contract storage update request. */ export function makeContractStorageUpdateRequest(seed = 1): ContractStorageUpdateRequest { - return new ContractStorageUpdateRequest(fr(seed), fr(seed + 1)); + return new ContractStorageUpdateRequest(fr(seed), fr(seed + 1), seed + 2); } /** @@ -669,6 +670,21 @@ export function makePublicKernelCircuitPrivateInputs(seed = 1): PublicKernelCirc return new PublicKernelCircuitPrivateInputs(makePublicKernelData(seed), makePublicCallData(seed + 0x1000)); } +export function makeCombineHints(seed = 1): CombineHints { + return CombineHints.from({ + sortedNoteHashes: makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, makeNoteHash, seed + 0x100), + sortedNoteHashesIndexes: makeTuple(MAX_NEW_NOTE_HASHES_PER_TX, i => i, seed + 0x200), + sortedUnencryptedLogsHashes: makeTuple(MAX_UNENCRYPTED_LOGS_PER_TX, makeLogHash, seed + 0x300), + sortedUnencryptedLogsHashesIndexes: makeTuple(MAX_UNENCRYPTED_LOGS_PER_TX, i => i, seed + 0x400), + sortedPublicDataUpdateRequests: makeTuple( + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + makePublicDataUpdateRequest, + seed + 0x300, + ), + sortedPublicDataUpdateRequestsIndexes: makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => i, seed + 0x400), + }); +} + /** * Makes arbitrary public kernel tail inputs. * @param seed - The seed to use for generating the public kernel inputs. @@ -682,6 +698,7 @@ export function makePublicKernelTailCircuitPrivateInputs(seed = 1): PublicKernel makeTuple(MAX_PUBLIC_DATA_HINTS, PublicDataHint.empty, seed + 0x100), PublicDataReadRequestHintsBuilder.empty(), makePartialStateReference(seed + 0x200), + makeCombineHints(seed + 0x300), ); } diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 3fc6ee3d64a..9b4cd85a9a7 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -168,7 +168,7 @@ describe('L1Publisher integration', () => { return tx; }; - const makeBloatedProcessedTx = (seed = 0x1) => { + const makeBloatedProcessedTx = (seed = 0x1): ProcessedTx => { const tx = mockTx(seed); const kernelOutput = KernelCircuitPublicInputs.empty(); kernelOutput.constants.txContext.chainId = fr(chainId); @@ -176,7 +176,7 @@ describe('L1Publisher integration', () => { kernelOutput.constants.historicalHeader = prevHeader; kernelOutput.end.publicDataUpdateRequests = makeTuple( MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - i => new PublicDataUpdateRequest(fr(i), fr(i + 10)), + i => new PublicDataUpdateRequest(fr(i), fr(i + 10), i + 20), seed + 0x500, ); @@ -192,7 +192,7 @@ describe('L1Publisher integration', () => { return processedTx; }; - const sendToL2 = async (content: Fr, recipientAddress: AztecAddress) => { + const sendToL2 = async (content: Fr, recipientAddress: AztecAddress): Promise => { // @todo @LHerskind version hardcoded here (update to bigint or field) const recipient = new L2Actor(recipientAddress, 1); // getting the 32 byte hex string representation of the content @@ -233,7 +233,7 @@ describe('L1Publisher integration', () => { l1ToL2Content: Fr[], recipientAddress: AztecAddress, deployerAddress: `0x${string}`, - ) => { + ): void => { if (!AZTEC_GENERATE_TEST_DATA) { return; } diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index 8aaa9345874..f8d31429f58 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -133,6 +133,10 @@ abstract class BaseField { return this.toBuffer().equals(ZERO_BUFFER); } + isEmpty(): boolean { + return this.isZero(); + } + toFriendlyJSON(): string { return this.toString(); } 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 ce4dfdcd75b..ead7212a1ca 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -7,6 +7,7 @@ import { CallContext, CallRequest, CallerContext, + type CombineHints, CombinedAccumulatedData, CombinedConstantData, ConstantRollupData, @@ -138,6 +139,7 @@ import type { CallContext as CallContextNoir, CallRequest as CallRequestNoir, CallerContext as CallerContextNoir, + CombineHints as CombineHintsNoir, CombinedAccumulatedData as CombinedAccumulatedDataNoir, CombinedConstantData as CombinedConstantDataNoir, ConstantRollupData as ConstantRollupDataNoir, @@ -1041,6 +1043,7 @@ export function mapPublicDataUpdateRequestFromNoir( return new PublicDataUpdateRequest( mapFieldFromNoir(publicDataUpdateRequest.leaf_slot), mapFieldFromNoir(publicDataUpdateRequest.new_value), + mapNumberFromNoir(publicDataUpdateRequest.counter), ); } @@ -1055,6 +1058,7 @@ export function mapPublicDataUpdateRequestToNoir( return { leaf_slot: mapFieldToNoir(publicDataUpdateRequest.leafSlot), new_value: mapFieldToNoir(publicDataUpdateRequest.newValue), + counter: mapNumberToNoir(publicDataUpdateRequest.sideEffectCounter), }; } @@ -1752,6 +1756,23 @@ export function mapPublicKernelCircuitPrivateInputsToNoir( }; } +export function mapCombineHintsToNoir(combineHints: CombineHints): CombineHintsNoir { + return { + sorted_note_hashes: mapTuple(combineHints.sortedNoteHashes, mapNoteHashToNoir), + sorted_note_hashes_indexes: mapTuple(combineHints.sortedNoteHashesIndexes, mapNumberToNoir), + sorted_unencrypted_logs_hashes: mapTuple(combineHints.sortedUnencryptedLogsHashes, mapLogHashToNoir), + sorted_unencrypted_logs_hashes_indexes: mapTuple(combineHints.sortedUnencryptedLogsHashesIndexes, mapNumberToNoir), + sorted_public_data_update_requests: mapTuple( + combineHints.sortedPublicDataUpdateRequests, + mapPublicDataUpdateRequestToNoir, + ), + sorted_public_data_update_requests_indexes: mapTuple( + combineHints.sortedPublicDataUpdateRequestsIndexes, + mapNumberToNoir, + ), + }; +} + export function mapPublicKernelTailCircuitPrivateInputsToNoir( inputs: PublicKernelTailCircuitPrivateInputs, ): PublicKernelTailCircuitPrivateInputsNoir { @@ -1764,6 +1785,7 @@ export function mapPublicKernelTailCircuitPrivateInputsToNoir( public_data_hints: mapTuple(inputs.publicDataHints, mapPublicDataHintToNoir), public_data_read_request_hints: mapPublicDataReadRequestHintsToNoir(inputs.publicDataReadRequestHints), start_state: mapPartialStateReferenceToNoir(inputs.startState), + combine_hints: mapCombineHintsToNoir(inputs.combineHints), }; } @@ -1792,6 +1814,7 @@ export function mapStorageUpdateRequestToNoir( return { storage_slot: mapFieldToNoir(storageUpdateRequest.storageSlot), new_value: mapFieldToNoir(storageUpdateRequest.newValue), + counter: mapNumberToNoir(storageUpdateRequest.sideEffectCounter), }; } /** diff --git a/yarn-project/prover-client/src/mocks/fixtures.ts b/yarn-project/prover-client/src/mocks/fixtures.ts index d8b144eeffb..81a1aff8a14 100644 --- a/yarn-project/prover-client/src/mocks/fixtures.ts +++ b/yarn-project/prover-client/src/mocks/fixtures.ts @@ -102,12 +102,12 @@ export const makeBloatedProcessedTx = async (builderDb: MerkleTreeOperations, se kernelOutput.constants.historicalHeader = await builderDb.buildInitialHeader(); kernelOutput.end.publicDataUpdateRequests = makeTuple( MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - i => new PublicDataUpdateRequest(fr(i), fr(i + 10)), + i => new PublicDataUpdateRequest(fr(i), fr(i + 10), i + 20), seed + 0x500, ); kernelOutput.end.publicDataUpdateRequests = makeTuple( MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - i => new PublicDataUpdateRequest(fr(i), fr(i + 10)), + i => new PublicDataUpdateRequest(fr(i), fr(i + 10), i + 20), seed + 0x600, ); 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 index e4853eca8d4..4f16d0d3db6 100644 --- 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 @@ -10,7 +10,7 @@ import { sortByCounterGetSortedHints, } from '@aztec/circuits.js'; -export function buildPrivateKernelTailHints(publicInputs: PrivateKernelCircuitPublicInputs) { +export function buildPrivateKernelTailHints(publicInputs: PrivateKernelCircuitPublicInputs): PrivateKernelTailHints { const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints( publicInputs.end.newNoteHashes, MAX_NEW_NOTE_HASHES_PER_TX, diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index 186e2ee560c..39900a113f0 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -61,7 +61,6 @@ import { type PublicExecutor, accumulateReturnValues, collectPublicDataReads, - collectPublicDataUpdateRequests, isPublicExecutionResult, } from '@aztec/simulator'; import { type MerkleTreeOperations } from '@aztec/world-state'; @@ -227,7 +226,7 @@ export abstract class AbstractPhaseManager { Gas, ] > { - let kernelOutput = previousPublicKernelOutput; + let kernelOutput: PublicKernelCircuitPublicInputs = previousPublicKernelOutput; const publicKernelInputs: PublicKernelCircuitPrivateInputs[] = []; const enqueuedCalls = this.extractEnqueuedPublicCalls(tx); @@ -258,8 +257,7 @@ export abstract class AbstractPhaseManager { while (executionStack.length) { const current = executionStack.pop()!; const isExecutionRequest = !isPublicExecutionResult(current); - // TODO(6052): Extract correct new counter from nested calls - const sideEffectCounter = lastSideEffectCounter(tx) + 1; + const sideEffectCounter = lastSideEffectCounter(kernelOutput) + 1; const availableGas = this.getAvailableGas(tx, kernelOutput); const pendingNullifiers = this.getSiloedPendingNullifiers(kernelOutput); @@ -511,9 +509,6 @@ export abstract class AbstractPhaseManager { publicInputs: PublicKernelCircuitPublicInputs, execResult: PublicExecutionResult, ) { - const { publicDataUpdateRequests } = PhaseIsRevertible[this.phase] - ? publicInputs.end - : publicInputs.endNonRevertibleData; const { publicDataReads } = publicInputs.validationRequests; // Convert ContractStorage* objects to PublicData* objects and sort them in execution order. @@ -522,13 +517,9 @@ export abstract class AbstractPhaseManager { const simPublicDataReads = collectPublicDataReads(execResult); - const simPublicDataUpdateRequests = collectPublicDataUpdateRequests(execResult); - // We only want to reorder the items from the public inputs of the // most recently processed top/enqueued call. - const effectSet = PhaseIsRevertible[this.phase] ? 'end' : 'endNonRevertibleData'; - const numReadsInKernel = arrayNonEmptyLength(publicDataReads, f => f.isEmpty()); const numReadsBeforeThisEnqueuedCall = numReadsInKernel - simPublicDataReads.length; publicInputs.validationRequests.publicDataReads = padArrayEnd( @@ -540,18 +531,6 @@ export abstract class AbstractPhaseManager { PublicDataRead.empty(), MAX_PUBLIC_DATA_READS_PER_TX, ); - - const numUpdatesInKernel = arrayNonEmptyLength(publicDataUpdateRequests, f => f.isEmpty()); - const numUpdatesBeforeThisEnqueuedCall = numUpdatesInKernel - simPublicDataUpdateRequests.length; - - publicInputs[effectSet].publicDataUpdateRequests = padArrayEnd( - [ - ...publicInputs[effectSet].publicDataUpdateRequests.slice(0, numUpdatesBeforeThisEnqueuedCall), - ...simPublicDataUpdateRequests, - ], - PublicDataUpdateRequest.empty(), - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); } } diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index 47d7e734a9b..2379ea9ec19 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -70,10 +70,8 @@ async function executeTopLevelPublicFunctionAvm( executionContext.commitmentsDb, ); - // TODO(6207): add sideEffectCounter to persistableState construction - // or modify the PersistableStateManager to manage rollbacks across enqueued-calls and transactions. + const startSideEffectCounter = executionContext.sideEffectCounter.current(); const worldStateJournal = new AvmPersistableStateManager(hostStorage); - const startSideEffectCounter = executionContext.execution.callContext.sideEffectCounter; for (const nullifier of executionContext.pendingNullifiers) { worldStateJournal.nullifiers.cache.appendSiloed(nullifier.value); } diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index 761f24b427e..d1a2a46e136 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -884,8 +884,8 @@ describe('public_processor', () => { PublicExecutionResultBuilder.fromPublicCallRequest({ request: publicCallRequests[1], contractStorageUpdateRequests: [ - new ContractStorageUpdateRequest(contractSlotA, fr(0x101), 14, baseContractAddress), - new ContractStorageUpdateRequest(contractSlotB, fr(0x151), 15, baseContractAddress), + new ContractStorageUpdateRequest(contractSlotA, fr(0x101), 10, baseContractAddress), + new ContractStorageUpdateRequest(contractSlotB, fr(0x151), 11, baseContractAddress), ], }).build({ startGasLeft: afterSetupGas, @@ -900,16 +900,16 @@ describe('public_processor', () => { from: teardown!.contractAddress, tx: makeFunctionCall('', baseContractAddress, makeSelector(5)), contractStorageUpdateRequests: [ - new ContractStorageUpdateRequest(contractSlotA, fr(0x102), 11, baseContractAddress), - new ContractStorageUpdateRequest(contractSlotC, fr(0x201), 12, baseContractAddress), + new ContractStorageUpdateRequest(contractSlotA, fr(0x103), 14, baseContractAddress), + new ContractStorageUpdateRequest(contractSlotC, fr(0x201), 15, baseContractAddress), ], }).build({ startGasLeft: teardownGas, endGasLeft: teardownGas, transactionFee }), PublicExecutionResultBuilder.fromFunctionCall({ from: teardown!.contractAddress, tx: makeFunctionCall('', baseContractAddress, makeSelector(5)), contractStorageUpdateRequests: [ - new ContractStorageUpdateRequest(contractSlotA, fr(0x103), 13, baseContractAddress), - new ContractStorageUpdateRequest(contractSlotB, fr(0x152), 15, baseContractAddress), + new ContractStorageUpdateRequest(contractSlotA, fr(0x102), 12, baseContractAddress), + new ContractStorageUpdateRequest(contractSlotB, fr(0x152), 13, baseContractAddress), ], }).build({ startGasLeft: teardownGas, endGasLeft: teardownGas, transactionFee }), ], @@ -973,13 +973,13 @@ describe('public_processor', () => { const txEffect = toTxEffect(processed[0], GasFees.default()); expect(arrayNonEmptyLength(txEffect.publicDataWrites, PublicDataWrite.isEmpty)).toEqual(3); expect(txEffect.publicDataWrites[0]).toEqual( - new PublicDataWrite(computePublicDataTreeLeafSlot(baseContractAddress, contractSlotC), fr(0x201)), + new PublicDataWrite(computePublicDataTreeLeafSlot(baseContractAddress, contractSlotB), fr(0x152)), ); expect(txEffect.publicDataWrites[1]).toEqual( new PublicDataWrite(computePublicDataTreeLeafSlot(baseContractAddress, contractSlotA), fr(0x103)), ); expect(txEffect.publicDataWrites[2]).toEqual( - new PublicDataWrite(computePublicDataTreeLeafSlot(baseContractAddress, contractSlotB), fr(0x152)), + new PublicDataWrite(computePublicDataTreeLeafSlot(baseContractAddress, contractSlotC), fr(0x201)), ); expect(txEffect.encryptedLogs.getTotalLogCount()).toBe(0); expect(txEffect.unencryptedLogs.getTotalLogCount()).toBe(0); @@ -1021,6 +1021,7 @@ describe('public_processor', () => { PublicDataUpdateRequest.from({ leafIndex: computeFeePayerBalanceLeafSlot(feePayer), newValue: new Fr(initialBalance - inclusionFee), + sideEffectCounter: 0, }), ); @@ -1071,6 +1072,7 @@ describe('public_processor', () => { PublicDataUpdateRequest.from({ leafIndex: computeFeePayerBalanceLeafSlot(feePayer), newValue: new Fr(initialBalance - inclusionFee), + sideEffectCounter: 0, }), ); @@ -1110,6 +1112,7 @@ describe('public_processor', () => { tx.data.publicInputs.end.publicDataUpdateRequests[0] = PublicDataUpdateRequest.from({ leafIndex: computeFeePayerBalanceLeafSlot(feePayer), newValue: new Fr(initialBalance), + sideEffectCounter: 0, }); const [processed, failed] = await processor.process([tx], 1, prover); @@ -1126,6 +1129,7 @@ describe('public_processor', () => { PublicDataUpdateRequest.from({ leafIndex: computeFeePayerBalanceLeafSlot(feePayer), newValue: new Fr(initialBalance - inclusionFee), + sideEffectCounter: 0, }), ); diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index a625fb47d14..80cb94211a9 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -207,7 +207,7 @@ export class PublicProcessor { finalPublicDataUpdateRequests[ existingBalanceWriteIndex > -1 ? existingBalanceWriteIndex : MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - ] = new PublicDataUpdateRequest(leafSlot, updatedBalance); + ] = new PublicDataUpdateRequest(leafSlot, updatedBalance, 0); return finalPublicDataUpdateRequests; } diff --git a/yarn-project/simulator/src/public/tail_phase_manager.ts b/yarn-project/simulator/src/public/tail_phase_manager.ts index 96ba9425ede..866c0da874f 100644 --- a/yarn-project/simulator/src/public/tail_phase_manager.ts +++ b/yarn-project/simulator/src/public/tail_phase_manager.ts @@ -1,21 +1,15 @@ import { type PublicKernelRequest, PublicKernelType, type Tx } from '@aztec/circuit-types'; import { - type Fr, + CombineHints, type GlobalVariables, type Header, type KernelCircuitPublicInputs, - type LogHash, - MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - type MAX_UNENCRYPTED_LOGS_PER_TX, - type NoteHash, type PublicKernelCircuitPublicInputs, PublicKernelTailCircuitPrivateInputs, mergeAccumulatedData, - sortByCounter, } from '@aztec/circuits.js'; -import { type Tuple } from '@aztec/foundation/serialize'; import { type PublicExecutor, type PublicStateDB } from '@aztec/simulator'; import { type MerkleTreeOperations } from '@aztec/world-state'; @@ -39,7 +33,7 @@ export class TailPhaseManager extends AbstractPhaseManager { override async handle(tx: Tx, previousPublicKernelOutput: PublicKernelCircuitPublicInputs) { this.log.verbose(`Processing tx ${tx.getTxHash()}`); - const [inputs, finalKernelOutput] = await this.runTailKernelCircuit(previousPublicKernelOutput).catch( + const [inputs, finalKernelOutput] = await this.simulate(previousPublicKernelOutput).catch( // the abstract phase manager throws if simulation gives error in non-revertible phase async err => { await this.publicStateDB.rollbackToCommit(); @@ -68,26 +62,6 @@ export class TailPhaseManager extends AbstractPhaseManager { }; } - private async runTailKernelCircuit( - previousOutput: PublicKernelCircuitPublicInputs, - ): Promise<[PublicKernelTailCircuitPrivateInputs, KernelCircuitPublicInputs]> { - // Temporary hack. Should sort them in the tail circuit. - previousOutput.end.unencryptedLogsHashes = this.sortLogsHashes( - previousOutput.end.unencryptedLogsHashes, - ); - const [inputs, output] = await this.simulate(previousOutput); - - // Temporary hack. Should sort them in the tail circuit. - const noteHashes = mergeAccumulatedData( - previousOutput.endNonRevertibleData.newNoteHashes, - previousOutput.end.newNoteHashes, - MAX_NEW_NOTE_HASHES_PER_TX, - ); - output.end.newNoteHashes = this.sortNoteHashes(noteHashes); - - return [inputs, output]; - } - private async simulate( previousOutput: PublicKernelCircuitPublicInputs, ): Promise<[PublicKernelTailCircuitPrivateInputs, KernelCircuitPublicInputs]> { @@ -99,11 +73,11 @@ export class TailPhaseManager extends AbstractPhaseManager { private async buildPrivateInputs(previousOutput: PublicKernelCircuitPublicInputs) { const previousKernel = this.getPreviousKernelData(previousOutput); - const { validationRequests, endNonRevertibleData, end } = previousOutput; + const { validationRequests, endNonRevertibleData: nonRevertibleData, end: revertibleData } = previousOutput; const pendingNullifiers = mergeAccumulatedData( - endNonRevertibleData.newNullifiers, - end.newNullifiers, + nonRevertibleData.newNullifiers, + revertibleData.newNullifiers, MAX_NEW_NULLIFIERS_PER_TX, ); @@ -118,8 +92,8 @@ export class TailPhaseManager extends AbstractPhaseManager { ); const pendingPublicDataWrites = mergeAccumulatedData( - endNonRevertibleData.publicDataUpdateRequests, - end.publicDataUpdateRequests, + nonRevertibleData.publicDataUpdateRequests, + revertibleData.publicDataUpdateRequests, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, ); @@ -136,6 +110,8 @@ export class TailPhaseManager extends AbstractPhaseManager { const currentState = await this.db.getStateReference(); + const hints = CombineHints.fromPublicData({ nonRevertibleData, revertibleData }); + return new PublicKernelTailCircuitPrivateInputs( previousKernel, nullifierReadRequestHints, @@ -143,15 +119,7 @@ export class TailPhaseManager extends AbstractPhaseManager { publicDataHints, publicDataReadRequestHints, currentState.partial, + hints, ); } - - private sortNoteHashes(noteHashes: Tuple): Tuple { - return sortByCounter(noteHashes).map(n => n.value) as Tuple; - } - - private sortLogsHashes(unencryptedLogsHashes: Tuple): Tuple { - // TODO(6052): logs here may have duplicate counters from nested calls - return sortByCounter(unencryptedLogsHashes); - } } diff --git a/yarn-project/simulator/src/public/utils.test.ts b/yarn-project/simulator/src/public/utils.test.ts index 3f791162bcd..0ca629d7ce3 100644 --- a/yarn-project/simulator/src/public/utils.test.ts +++ b/yarn-project/simulator/src/public/utils.test.ts @@ -1,26 +1,26 @@ -import { mockTx } from '@aztec/circuit-types'; import { Fr } from '@aztec/circuits.js'; +import { makePublicKernelCircuitPublicInputs } from '@aztec/circuits.js/testing'; import { lastSideEffectCounter } from './utils.js'; describe('sequencer utils', () => { describe('lastSideEffectCounter', () => { it('correctly identifies the highest side effect counter in a transaction', () => { - const tx = mockTx(); - // mockTx creates a Tx with side effect counts of all 0 - expect(lastSideEffectCounter(tx)).toBe(0); + const inputs = makePublicKernelCircuitPublicInputs(); - tx.data.forPublic!.endNonRevertibleData.newNoteHashes.at(-1)!.counter = 8; - expect(lastSideEffectCounter(tx)).toBe(8); + const startingCounter = lastSideEffectCounter(inputs); - tx.data.forPublic!.endNonRevertibleData.publicCallStack.at(-1)!.startSideEffectCounter = new Fr(9); - expect(lastSideEffectCounter(tx)).toBe(9); + inputs.endNonRevertibleData.newNoteHashes.at(-1)!.counter = startingCounter + 1; + expect(lastSideEffectCounter(inputs)).toBe(startingCounter + 1); - tx.data.forPublic!.end.newNoteHashes.at(-1)!.counter = 10; - expect(lastSideEffectCounter(tx)).toBe(10); + inputs.endNonRevertibleData.publicCallStack.at(-1)!.startSideEffectCounter = new Fr(startingCounter + 2); + expect(lastSideEffectCounter(inputs)).toBe(startingCounter + 2); - tx.data.forPublic!.end.newNullifiers.at(-1)!.counter = 11; - expect(lastSideEffectCounter(tx)).toBe(11); + inputs.end.newNoteHashes.at(-1)!.counter = startingCounter + 3; + expect(lastSideEffectCounter(inputs)).toBe(startingCounter + 3); + + inputs.end.newNullifiers.at(-1)!.counter = startingCounter + 4; + expect(lastSideEffectCounter(inputs)).toBe(startingCounter + 4); }); }); }); diff --git a/yarn-project/simulator/src/public/utils.ts b/yarn-project/simulator/src/public/utils.ts index f824cf9e92f..2ca86c7ddb6 100644 --- a/yarn-project/simulator/src/public/utils.ts +++ b/yarn-project/simulator/src/public/utils.ts @@ -1,31 +1,37 @@ -import { type Tx } from '@aztec/circuit-types'; -import { CallRequest } from '@aztec/circuits.js'; +import { type PublicKernelCircuitPublicInputs } from '@aztec/circuits.js'; /** * Looks at the side effects of a transaction and returns the highest counter * @param tx - A transaction * @returns The highest side effect counter in the transaction so far */ -export function lastSideEffectCounter(tx: Tx): number { - const data = tx.data.forPublic!; +export function lastSideEffectCounter(inputs: PublicKernelCircuitPublicInputs): number { const sideEffectCounters = [ - ...data.endNonRevertibleData.newNoteHashes, - ...data.endNonRevertibleData.newNullifiers, - ...data.endNonRevertibleData.unencryptedLogsHashes, - ...data.endNonRevertibleData.publicCallStack, - ...data.end.newNoteHashes, - ...data.end.newNullifiers, - ...data.end.unencryptedLogsHashes, - ...data.end.publicCallStack, + ...inputs.endNonRevertibleData.newNoteHashes, + ...inputs.endNonRevertibleData.newNullifiers, + ...inputs.endNonRevertibleData.noteEncryptedLogsHashes, + ...inputs.endNonRevertibleData.encryptedLogsHashes, + ...inputs.endNonRevertibleData.unencryptedLogsHashes, + ...inputs.endNonRevertibleData.publicCallStack, + ...inputs.endNonRevertibleData.publicDataUpdateRequests, + ...inputs.end.newNoteHashes, + ...inputs.end.newNullifiers, + ...inputs.end.noteEncryptedLogsHashes, + ...inputs.end.encryptedLogsHashes, + ...inputs.end.unencryptedLogsHashes, + ...inputs.end.publicCallStack, + ...inputs.end.publicDataUpdateRequests, ]; let max = 0; for (const sideEffect of sideEffectCounters) { - if (sideEffect instanceof CallRequest) { + if ('startSideEffectCounter' in sideEffect) { // 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 { + } else if ('counter' in sideEffect) { max = Math.max(max, sideEffect.counter); + } else { + throw new Error('Unknown side effect type'); } }