From 76a03cdfdf2dde75ed4f536ec3c6cefa615134d9 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 9 Aug 2024 08:59:48 +0000 Subject: [PATCH 1/7] Move siloing to reset. --- .../src/core/libraries/ConstantsGen.sol | 9 +- .../private-kernel-lib/src/components/mod.nr | 1 + .../components/previous_kernel_validator.nr | 71 +++-- .../previous_kernel_validator_hints.nr | 22 +- ...e_kernel_circuit_public_inputs_composer.nr | 53 +--- .../src/components/reset_output_composer.nr | 104 ++++++- .../reset_output_hints.nr | 108 +++++++ ..._propagated_note_hash_indexes_for_logs.nr} | 4 +- .../squash_transient_data.nr | 2 +- .../src/components/reset_output_validator.nr | 227 +++++++++++++++ .../src/components/tail_output_composer.nr | 3 +- .../src/components/tail_output_validator.nr | 98 ++----- .../tail_output_hints.nr | 55 +--- .../validate_value_transformation.nr | 23 -- .../tail_to_public_output_composer.nr | 2 +- .../tail_to_public_output_validator.nr | 142 ++++------ .../tail_to_public_output_hints.nr | 67 +---- .../src/private_kernel_reset.nr | 212 ++++++++++++-- .../src/private_kernel_tail.nr | 165 +---------- .../src/private_kernel_tail_to_public.nr | 114 ++------ .../validate_no_transient_data.nr | 27 +- .../tests/tail_output_composer_builder/mod.nr | 6 - .../tail_output_validator_builder/mod.nr | 21 +- .../validate_accumulated_values.nr | 123 -------- .../validate_gas_used.nr | 2 +- ...alidate_propagated_sorted_siloed_values.nr | 130 --------- .../validate_propagated_sorted_values.nr | 79 ++++++ .../validate_propagated_values.nr | 170 +++++++++++ .../mod.nr | 6 - .../tail_to_public_output_composer.nr | 86 +++--- ...tail_to_public_output_validator_builder.nr | 8 +- .../src/main.nr | 13 +- .../crates/private-kernel-reset/src/main.nr | 13 +- .../crates/reset-kernel-lib/src/lib.nr | 8 +- .../src/reset/transient_data.nr | 2 - .../crates/types/src/constants.nr | 9 +- .../crates/types/src/tests/fixture_builder.nr | 21 +- .../crates/types/src/utils/arrays.nr | 2 + .../assert_sorted_transformed_value_array.nr | 12 +- .../assert_split_transformed_value_arrays.nr | 264 ++++++++++++++++++ .../generate_variants.js | 5 +- .../reset_variants.json | 36 ++- yarn-project/bb-prover/src/stats.ts | 2 + yarn-project/circuit-types/src/stats/stats.ts | 1 + yarn-project/circuits.js/src/constants.gen.ts | 9 +- .../src/scripts/generate_reset_variants.ts | 36 ++- .../src/artifacts.ts | 4 + .../src/scripts/generate_ts_from_abi.ts | 4 +- .../noir-protocol-circuits-types/src/vks.ts | 4 + .../hints/build_private_kernel_reset_hints.ts | 31 +- .../src/kernel_prover/hints/needs_reset.ts | 37 +-- .../pxe/src/kernel_prover/kernel_prover.ts | 8 +- 52 files changed, 1554 insertions(+), 1107 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr rename noir-projects/noir-protocol-circuits/crates/{reset-kernel-lib/src/reset/transient_data/transient_data_reset_hints.nr => private-kernel-lib/src/components/reset_output_composer/reset_output_hints/get_transient_or_propagated_note_hash_indexes_for_logs.nr} (81%) rename noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/{ => reset_output_hints}/squash_transient_data.nr (94%) create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_value_transformation.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_accumulated_values.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_siloed_values.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 4b527049baed..335e6b1bb20a 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -74,10 +74,11 @@ library Constants { uint256 internal constant PRIVATE_KERNEL_INIT_INDEX = 0; uint256 internal constant PRIVATE_KERNEL_INNER_INDEX = 1; uint256 internal constant PRIVATE_KERNEL_RESET_FULL_INDEX = 2; - uint256 internal constant PRIVATE_KERNEL_RESET_BIG_INDEX = 3; - uint256 internal constant PRIVATE_KERNEL_RESET_MEDIUM_INDEX = 4; - uint256 internal constant PRIVATE_KERNEL_RESET_SMALL_INDEX = 5; - uint256 internal constant PRIVATE_KERNEL_RESET_TINY_INDEX = 6; + uint256 internal constant PRIVATE_KERNEL_RESET_FULL_INNER_INDEX = 3; + uint256 internal constant PRIVATE_KERNEL_RESET_BIG_INDEX = 4; + uint256 internal constant PRIVATE_KERNEL_RESET_MEDIUM_INDEX = 5; + uint256 internal constant PRIVATE_KERNEL_RESET_SMALL_INDEX = 6; + uint256 internal constant PRIVATE_KERNEL_RESET_TINY_INDEX = 7; uint256 internal constant PRIVATE_KERNEL_TAIL_INDEX = 10; uint256 internal constant PRIVATE_KERNEL_TAIL_TO_PUBLIC_INDEX = 11; uint256 internal constant EMPTY_NESTED_INDEX = 12; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/mod.nr index 49a01195740f..f5c1113c75e1 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/mod.nr @@ -3,6 +3,7 @@ mod private_call_data_validator; mod private_kernel_circuit_output_validator; mod private_kernel_circuit_public_inputs_composer; mod reset_output_composer; +mod reset_output_validator; mod tail_output_composer; mod tail_output_validator; mod tail_to_public_output_composer; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr index 43c4065906c1..c77beeb1649d 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr @@ -2,8 +2,8 @@ mod previous_kernel_validator_hints; use crate::components::previous_kernel_validator::previous_kernel_validator_hints::{generate_previous_kernel_validator_hints, PreviousKernelValidatorHints}; use dep::types::{ - abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, utils::arrays::array_length, - traits::is_empty + abis::{kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::ScopedEncryptedLogHash}, + address::AztecAddress, traits::is_empty, utils::arrays::array_length }; struct PreviousKernelValidator { @@ -30,18 +30,20 @@ impl PreviousKernelValidator { fn validate_common(self) { self.validate_empty_private_call_stack(); self.verify_empty_validation_requests(); + self.verify_sorted_siloed_values(); self.validate_no_transient_data(); } fn validate_empty_private_call_stack(self) { - assert_eq( - array_length(self.previous_kernel.end.private_call_stack), 0, "Private call stack must be empty when executing the tail circuit" + // Only need to check the first item as the kernel circuits always append items to the arrays properly. + assert( + is_empty(self.previous_kernel.end.private_call_stack[0]), "Private call stack must be empty when executing the tail circuit" ); } fn validate_empty_data(self) { - assert_eq( - array_length(self.previous_kernel.end.public_call_requests), 0, "Public call stack must be empty when executing the tail circuit" + assert( + is_empty(self.previous_kernel.end.public_call_requests[0]), "Public call stack must be empty when executing the tail circuit" ); assert( is_empty(self.previous_kernel.public_teardown_call_request), "Public teardown call request must be empty when executing the tail circuit" @@ -57,9 +59,9 @@ impl PreviousKernelValidator { } fn validate_non_empty_data(self) { - let len = array_length(self.previous_kernel.end.public_call_requests); assert( - (len != 0) | !is_empty(self.previous_kernel.public_teardown_call_request), "Must have public calls when exporting public kernel data from the tail circuit" + !is_empty(self.previous_kernel.end.public_call_requests[0]) + | !is_empty(self.previous_kernel.public_teardown_call_request), "Must have public calls when exporting public kernel data from the tail circuit" ); assert( @@ -74,31 +76,62 @@ impl PreviousKernelValidator { } fn verify_empty_validation_requests(self) { - assert_eq( - array_length(self.previous_kernel.validation_requests.note_hash_read_requests), 0, "Non empty note hash read requests" + assert( + is_empty(self.previous_kernel.validation_requests.note_hash_read_requests[0]), "Non empty note hash read requests" ); - assert_eq( - array_length(self.previous_kernel.validation_requests.nullifier_read_requests), 0, "Non empty nullifier read requests" + assert( + is_empty(self.previous_kernel.validation_requests.nullifier_read_requests[0]), "Non empty nullifier read requests" ); + assert( + is_empty(self.previous_kernel.validation_requests.scoped_key_validation_requests_and_generators[0]), "Non empty key validation requests" + ); + } + + fn verify_sorted_siloed_values(self) { + // Check that the following data are already siloed or sorted in the reset circuit. + // We only need to check the last non-empty item, since the reset circuit always processes from index 0. + + let num_note_hashes = array_length(self.previous_kernel.end.note_hashes); + if num_note_hashes != 0 { + let note_hash = self.previous_kernel.end.note_hashes[num_note_hashes - 1]; + assert_eq( + note_hash.contract_address, AztecAddress::zero(), "note hashes have not been siloed in a reset" + ); + } + + let num_nullifiers = array_length(self.previous_kernel.end.nullifiers); + let nullifier = self.previous_kernel.end.nullifiers[num_nullifiers - 1]; // - 1 without checking because there's at least 1 nullifier. assert_eq( - array_length(self.previous_kernel.validation_requests.scoped_key_validation_requests_and_generators), 0, "Non empty key validation requests" + nullifier.contract_address, AztecAddress::zero(), "nullifiers have not been siloed in a reset" + ); + + // Note logs are not siloed, but they are sorted and their note_hash_counter should've been set to 0 in the reset circuit. + let num_note_logs = array_length(self.previous_kernel.end.note_encrypted_logs_hashes); + if num_note_logs != 0 { + let note_log = self.previous_kernel.end.note_encrypted_logs_hashes[num_note_logs - 1]; + assert_eq(note_log.note_hash_counter, 0, "note logs have not been sorted in a reset"); + } + + // We need to check the entire array because randomness can be 0 for encrypted logs if the app wants to reveal the actual contract address. + assert( + self.previous_kernel.end.encrypted_logs_hashes.all(|h: ScopedEncryptedLogHash| h.log_hash.randomness == 0), "encrypted logs have not been siloed in a reset" ); } fn validate_no_transient_data(self) { let nullifiers = self.previous_kernel.end.nullifiers; - let inner_note_hashes = self.previous_kernel.end.note_hashes; - let siloed_note_hashes = self.hints.siloed_note_hashes; + let note_hashes = self.previous_kernel.end.note_hashes; let note_hash_indexes_for_nullifiers = self.hints.note_hash_indexes_for_nullifiers; for i in 0..nullifiers.len() { - let nullified_note_hash = nullifiers[i].nullifier.note_hash; + let nullifier = nullifiers[i]; + let nullified_note_hash = nullifier.nullifier.note_hash; if nullified_note_hash != 0 { - let note_hash_index = note_hash_indexes_for_nullifiers[i]; + let note_hash = note_hashes[note_hash_indexes_for_nullifiers[i]]; assert_eq( - nullified_note_hash, siloed_note_hashes[note_hash_index], "Hinted siloed note hash does not match nullified note hash" + note_hash.value(), nullified_note_hash, "Hinted siloed note hash does not match nullified note hash" ); assert( - inner_note_hashes[note_hash_index].counter() < nullifiers[i].counter(), "Cannot link a note hash emitted after a nullifier" + note_hash.counter() < nullifier.counter(), "Cannot link a note hash emitted after a nullifier" ); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator/previous_kernel_validator_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator/previous_kernel_validator_hints.nr index 1d0c9cf81996..f2106337cae6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator/previous_kernel_validator_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator/previous_kernel_validator_hints.nr @@ -1,32 +1,26 @@ use dep::types::{ - abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, hash::silo_note_hash, - utils::arrays::{find_index, sort_get_order_hints_asc} + abis::{kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, note_hash::ScopedNoteHash}, + constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, utils::arrays::find_index }; struct PreviousKernelValidatorHints { - siloed_note_hashes: [Field; MAX_NOTE_HASHES_PER_TX], note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX], } unconstrained pub fn generate_previous_kernel_validator_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> PreviousKernelValidatorHints { - let mut siloed_note_hashes = [0; MAX_NOTE_HASHES_PER_TX]; - let unsilod_note_hashes = previous_kernel.end.note_hashes; - let sorted_note_hash_hints = sort_get_order_hints_asc(unsilod_note_hashes); - let tx_hash = previous_kernel.end.nullifiers[0].value(); // First nullifier is tx hash. - for i in 0..unsilod_note_hashes.len() { - siloed_note_hashes[i] = silo_note_hash(unsilod_note_hashes[i], tx_hash, sorted_note_hash_hints[i].sorted_index); - } - let mut note_hash_indexes_for_nullifiers = [0; MAX_NULLIFIERS_PER_TX]; + let note_hashes = previous_kernel.end.note_hashes; let nullifiers = previous_kernel.end.nullifiers; for i in 0..nullifiers.len() { let nullified_note_hash = nullifiers[i].nullifier.note_hash; - let note_hash_index = find_index(siloed_note_hashes, |n: Field| n == nullified_note_hash); + let note_hash_index = find_index( + note_hashes, + |n: ScopedNoteHash| n.value() == nullified_note_hash + ); if (nullified_note_hash != 0) & (note_hash_index != MAX_NOTE_HASHES_PER_TX) { note_hash_indexes_for_nullifiers[i] = note_hash_index; } } - PreviousKernelValidatorHints { siloed_note_hashes, note_hash_indexes_for_nullifiers } + PreviousKernelValidatorHints { note_hash_indexes_for_nullifiers } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr index 092c51498945..2884124dd587 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr @@ -10,8 +10,7 @@ use dep::types::{ MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTE_HASHES_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL }, - hash::{silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, traits::is_empty, - transaction::tx_request::TxRequest, + traits::is_empty, transaction::tx_request::TxRequest, utils::arrays::{array_length, array_to_bounded_vec, sort_by_counters_asc, sort_by_counters_desc} }; @@ -100,9 +99,11 @@ impl PrivateKernelCircuitPublicInputsComposer { *self } - pub fn sort_and_silo(&mut self) { - self.sort_ordered_values(); - self.silo_scoped_values(); + pub fn sort_ordered_values(&mut self) { + // Note hashes, nullifiers, note_encrypted_logs_hashes, and encrypted_logs_hashes are sorted in the reset circuit. + self.public_inputs.end.l2_to_l1_msgs.storage = sort_by_counters_asc(self.public_inputs.end.l2_to_l1_msgs.storage); + self.public_inputs.end.unencrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.unencrypted_logs_hashes.storage); + self.public_inputs.end.public_call_requests.storage = sort_by_counters_desc(self.public_inputs.end.public_call_requests.storage); } pub fn finish(self) -> PrivateKernelCircuitPublicInputs { @@ -262,46 +263,4 @@ impl PrivateKernelCircuitPublicInputsComposer { self.public_inputs.fee_payer = source.storage_contract_address; } } - - fn sort_ordered_values(&mut self) { - self.public_inputs.end.note_hashes.storage = sort_by_counters_asc(self.public_inputs.end.note_hashes.storage); - self.public_inputs.end.nullifiers.storage = sort_by_counters_asc(self.public_inputs.end.nullifiers.storage); - self.public_inputs.end.l2_to_l1_msgs.storage = sort_by_counters_asc(self.public_inputs.end.l2_to_l1_msgs.storage); - self.public_inputs.end.note_encrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.note_encrypted_logs_hashes.storage); - self.public_inputs.end.encrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.encrypted_logs_hashes.storage); - self.public_inputs.end.unencrypted_logs_hashes.storage = sort_by_counters_asc(self.public_inputs.end.unencrypted_logs_hashes.storage); - self.public_inputs.end.public_call_requests.storage = sort_by_counters_desc(self.public_inputs.end.public_call_requests.storage); - } - - fn silo_scoped_values(&mut self) { - self.silo_note_hashes(); - self.silo_nullifiers(); - self.mask_encrypted_logs(); - } - - fn silo_note_hashes(&mut self) { - let first_nullifier = self.public_inputs.end.nullifiers.get_unchecked(0).value(); - let note_hashes = self.public_inputs.end.note_hashes.storage; - for i in 0..note_hashes.len() { - self.public_inputs.end.note_hashes.storage[i].note_hash.value = silo_note_hash( - note_hashes[i], - first_nullifier, - i - ); - } - } - - fn silo_nullifiers(&mut self) { - let nullifiers = self.public_inputs.end.nullifiers.storage; - for i in 0..nullifiers.len() { - self.public_inputs.end.nullifiers.storage[i].nullifier.value = silo_nullifier(nullifiers[i]); - } - } - - fn mask_encrypted_logs(&mut self) { - let logs = self.public_inputs.end.encrypted_logs_hashes.storage; - for i in 0..logs.len() { - self.public_inputs.end.encrypted_logs_hashes.storage[i].contract_address = mask_encrypted_log_hash(logs[i]); - } - } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr index 0283674772ed..81f7e0596f34 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr @@ -1,42 +1,118 @@ -mod squash_transient_data; +mod reset_output_hints; -use crate::components::reset_output_composer::squash_transient_data::squash_transient_data; +use crate::components::reset_output_composer::{reset_output_hints::{generate_reset_output_hints, ResetOutputHints}}; use dep::types::{ abis::{ - kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::NoteLogHash, - note_hash::ScopedNoteHash, nullifier::ScopedNullifier + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + log_hash::{NoteLogHash, ScopedEncryptedLogHash}, note_hash::ScopedNoteHash, + nullifier::ScopedNullifier }, - constants::{MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX} + address::AztecAddress, + constants::{ + MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASHES_PER_TX, + MAX_NULLIFIERS_PER_TX +}, + hash::{mask_encrypted_log_hash, silo_note_hash, silo_nullifier}, utils::arrays::sort_by_counters_asc }; struct PrivateKernelResetOutputs { note_hashes: [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX], nullifiers: [ScopedNullifier; MAX_NULLIFIERS_PER_TX], note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], } struct ResetOutputComposer { - output: PrivateKernelResetOutputs, + previous_kernel: PrivateKernelCircuitPublicInputs, + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32, + hints: ResetOutputHints, } impl ResetOutputComposer { pub fn new( previous_kernel: PrivateKernelCircuitPublicInputs, transient_nullifier_indexes_for_note_hashes: [u32; MAX_NOTE_HASHES_PER_TX], - transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX] + transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX], + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32 ) -> Self { - let (note_hashes, nullifiers, note_encrypted_log_hashes) = squash_transient_data( - previous_kernel.end.note_hashes, - previous_kernel.end.nullifiers, - previous_kernel.end.note_encrypted_logs_hashes, + let hints = generate_reset_output_hints( + previous_kernel, transient_nullifier_indexes_for_note_hashes, transient_note_hash_indexes_for_nullifiers ); - let output = PrivateKernelResetOutputs { note_hashes, nullifiers, note_encrypted_log_hashes }; - ResetOutputComposer { output } + ResetOutputComposer { previous_kernel, note_hash_siloing_amount, nullifier_siloing_amount, encrypted_log_siloing_amount, hints } } pub fn finish(self) -> PrivateKernelResetOutputs { - self.output + let note_hashes = if self.note_hash_siloing_amount == 0 { + self.hints.kept_note_hashes + } else { + self.get_sorted_siloed_note_hashes() + }; + + let nullifiers = if self.nullifier_siloing_amount == 0 { + self.hints.kept_nullifiers + } else { + self.get_sorted_siloed_nullifiers() + }; + + let note_encrypted_log_hashes = if self.note_hash_siloing_amount == 0 { + self.hints.kept_note_encrypted_log_hashes + } else { + self.get_sorted_note_encrypted_log_hashes() + }; + + let encrypted_log_hashes = if self.encrypted_log_siloing_amount == 0 { + self.previous_kernel.end.encrypted_logs_hashes + } else { + self.get_sorted_masked_encrypted_log_hashes() + }; + + PrivateKernelResetOutputs { note_hashes, nullifiers, note_encrypted_log_hashes, encrypted_log_hashes } + } + + fn get_sorted_siloed_note_hashes(self) -> [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX] { + let mut note_hashes = sort_by_counters_asc(self.hints.kept_note_hashes); + let first_nullifier = self.previous_kernel.end.nullifiers[0].value(); + for i in 0..note_hashes.len() { + note_hashes[i].note_hash.value = silo_note_hash( + note_hashes[i], + first_nullifier, + i + ); + note_hashes[i].contract_address = AztecAddress::zero(); + } + note_hashes + } + + fn get_sorted_siloed_nullifiers(self) -> [ScopedNullifier; MAX_NULLIFIERS_PER_TX] { + let mut nullifiers = sort_by_counters_asc(self.hints.kept_nullifiers); + for i in 0..nullifiers.len() { + nullifiers[i].nullifier.value = silo_nullifier(nullifiers[i]); + nullifiers[i].nullifier.note_hash = 0; + nullifiers[i].contract_address = AztecAddress::zero(); + } + nullifiers + } + + fn get_sorted_note_encrypted_log_hashes(self) -> [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX] { + let mut log_hashes = sort_by_counters_asc(self.hints.kept_note_encrypted_log_hashes); + for i in 0..log_hashes.len() { + log_hashes[i].note_hash_counter = 0; + } + log_hashes + } + + fn get_sorted_masked_encrypted_log_hashes(self) -> [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX] { + let mut log_hashes = sort_by_counters_asc(self.previous_kernel.end.encrypted_logs_hashes); + for i in 0..log_hashes.len() { + log_hashes[i].contract_address = mask_encrypted_log_hash(log_hashes[i]); + log_hashes[i].log_hash.randomness = 0; + } + log_hashes } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr new file mode 100644 index 000000000000..4895780486cd --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr @@ -0,0 +1,108 @@ +mod get_transient_or_propagated_note_hash_indexes_for_logs; +mod squash_transient_data; + +use crate::components::reset_output_composer::reset_output_hints::{ + get_transient_or_propagated_note_hash_indexes_for_logs::get_transient_or_propagated_note_hash_indexes_for_logs, + squash_transient_data::squash_transient_data +}; +use dep::types::{ + abis::{ + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + log_hash::{NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, + nullifier::ScopedNullifier +}, + address::AztecAddress, + constants::{ + MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASHES_PER_TX, + MAX_NULLIFIERS_PER_TX +}, + hash::{mask_encrypted_log_hash, silo_note_hash, silo_nullifier}, + utils::arrays::{OrderHint, sort_get_order_hints_asc} +}; + +struct ResetOutputHints { + // note_hashes + kept_note_hashes: [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX], + siloed_note_hashes: [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX], + sorted_note_hash_hints: [OrderHint; MAX_NOTE_HASHES_PER_TX], + // nullifiers + kept_nullifiers: [ScopedNullifier; MAX_NULLIFIERS_PER_TX], + siloed_nullifiers: [ScopedNullifier; MAX_NULLIFIERS_PER_TX], + sorted_nullifier_hints: [OrderHint; MAX_NULLIFIERS_PER_TX], + // note_encrypted_log_hashes + kept_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + exposed_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + sorted_note_encrypted_log_hash_hints: [OrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + transient_or_propagated_note_hash_indexes_for_logs: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + // encrypted_log_hashes + masked_encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], + sorted_encrypted_log_hash_hints: [OrderHint; MAX_ENCRYPTED_LOGS_PER_TX], +} + +pub fn generate_reset_output_hints( + previous_kernel: PrivateKernelCircuitPublicInputs, + transient_nullifier_indexes_for_note_hashes: [u32; MAX_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX] +) -> ResetOutputHints { + let (kept_note_hashes, kept_nullifiers, kept_note_encrypted_log_hashes) = squash_transient_data( + previous_kernel.end.note_hashes, + previous_kernel.end.nullifiers, + previous_kernel.end.note_encrypted_logs_hashes, + transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers + ); + + // note_hashes + let mut siloed_note_hashes = kept_note_hashes; + let tx_hash = previous_kernel.end.nullifiers[0].value(); // First nullifier is tx hash. + let sorted_note_hash_hints = sort_get_order_hints_asc(kept_note_hashes); + for i in 0..kept_note_hashes.len() { + siloed_note_hashes[i].note_hash.value = silo_note_hash(kept_note_hashes[i], tx_hash, sorted_note_hash_hints[i].sorted_index); + siloed_note_hashes[i].contract_address = AztecAddress::zero(); + } + + // nullifiers + let mut siloed_nullifiers = kept_nullifiers; + for i in 0..kept_nullifiers.len() { + siloed_nullifiers[i].nullifier.value = silo_nullifier(kept_nullifiers[i]); + siloed_nullifiers[i].nullifier.note_hash = 0; + siloed_nullifiers[i].contract_address = AztecAddress::zero(); + } + let sorted_nullifier_hints = sort_get_order_hints_asc(kept_nullifiers); + + // note_encrypted_log_hashes + let mut exposed_note_encrypted_log_hashes = kept_note_encrypted_log_hashes; + for i in 0..exposed_note_encrypted_log_hashes.len() { + exposed_note_encrypted_log_hashes[i].note_hash_counter = 0; + } + let sorted_note_encrypted_log_hash_hints = sort_get_order_hints_asc(kept_note_encrypted_log_hashes); + let transient_or_propagated_note_hash_indexes_for_logs = get_transient_or_propagated_note_hash_indexes_for_logs( + previous_kernel.end.note_encrypted_logs_hashes, + previous_kernel.end.note_hashes, + kept_note_hashes + ); + + // encrypted_log_hashes + let encrypted_logs_hashes = previous_kernel.end.encrypted_logs_hashes; + let mut masked_encrypted_log_hashes = encrypted_logs_hashes; + for i in 0..masked_encrypted_log_hashes.len() { + masked_encrypted_log_hashes[i].contract_address = mask_encrypted_log_hash(encrypted_logs_hashes[i]); + masked_encrypted_log_hashes[i].log_hash.randomness = 0; + } + let sorted_encrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.encrypted_logs_hashes); + + ResetOutputHints { + kept_note_hashes, + siloed_note_hashes, + sorted_note_hash_hints, + kept_nullifiers, + siloed_nullifiers, + sorted_nullifier_hints, + kept_note_encrypted_log_hashes, + exposed_note_encrypted_log_hashes, + sorted_note_encrypted_log_hash_hints, + transient_or_propagated_note_hash_indexes_for_logs, + masked_encrypted_log_hashes, + sorted_encrypted_log_hash_hints + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data/transient_data_reset_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints/get_transient_or_propagated_note_hash_indexes_for_logs.nr similarity index 81% rename from noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data/transient_data_reset_hints.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints/get_transient_or_propagated_note_hash_indexes_for_logs.nr index c41015551bdf..8a07d134d28e 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data/transient_data_reset_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints/get_transient_or_propagated_note_hash_indexes_for_logs.nr @@ -1,6 +1,6 @@ -use dep::types::{abis::{note_hash::ScopedNoteHash, log_hash::NoteLogHash}}; +use dep::types::{abis::{log_hash::NoteLogHash, note_hash::ScopedNoteHash}}; -unconstrained pub fn get_transient_or_propagated_note_hash_indexes_for_logs( +pub fn get_transient_or_propagated_note_hash_indexes_for_logs( note_logs: [NoteLogHash; NUM_LOGS], note_hashes: [ScopedNoteHash; NUM_NOTE_HASHES], expected_note_hashes: [ScopedNoteHash; NUM_NOTE_HASHES] diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/squash_transient_data.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints/squash_transient_data.nr similarity index 94% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/squash_transient_data.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints/squash_transient_data.nr index 5cd5ed476579..bdb643f548b8 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/squash_transient_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints/squash_transient_data.nr @@ -1,6 +1,6 @@ use dep::types::abis::{note_hash::ScopedNoteHash, nullifier::ScopedNullifier, log_hash::NoteLogHash}; -unconstrained pub fn squash_transient_data( +unconstrained pub fn squash_transient_data( note_hashes: [ScopedNoteHash; M], nullifiers: [ScopedNullifier; N], logs: [NoteLogHash; P], diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr new file mode 100644 index 000000000000..64fc7137923c --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr @@ -0,0 +1,227 @@ +use crate::components::{reset_output_composer::{PrivateKernelResetOutputs, ResetOutputHints}}; +use dep::reset_kernel_lib::verify_squashed_transient_data; +use dep::types::{ + abis::{ + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + log_hash::{NoteLogHash, ScopedEncryptedLogHash}, nullifier::ScopedNullifier +}, + constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, + hash::{mask_encrypted_log_hash, silo_note_hash, silo_nullifier}, traits::is_empty, + utils::arrays::assert_sorted_transformed_value_array +}; + +struct ResetOutputValidator { + output: PrivateKernelResetOutputs, + previous_kernel: PrivateKernelCircuitPublicInputs, + transient_nullifier_indexes_for_note_hashes: [u32; MAX_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX], + split_counter: u32, + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32, + hints: ResetOutputHints, +} + +impl ResetOutputValidator { + pub fn new( + output: PrivateKernelResetOutputs, + previous_kernel: PrivateKernelCircuitPublicInputs, + transient_nullifier_indexes_for_note_hashes: [u32; MAX_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX], + split_counter: u32, + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32, + hints: ResetOutputHints + ) -> Self { + ResetOutputValidator { + output, + previous_kernel, + transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers, + split_counter, + note_hash_siloing_amount, + nullifier_siloing_amount, + encrypted_log_siloing_amount, + hints + } + } + + pub fn validate(self) { + self.validate_squashed_data(); + + if self.note_hash_siloing_amount == 0 { + assert_eq(self.output.note_hashes, self.hints.kept_note_hashes, "output note hashes mismatch"); + assert_eq( + self.output.note_encrypted_log_hashes, self.hints.kept_note_encrypted_log_hashes, "output note logs mismatch" + ); + } else { + self.validate_sorted_siloed_note_hashes(); + self.validate_sorted_note_logs(); + } + + if self.nullifier_siloing_amount == 0 { + assert_eq(self.output.nullifiers, self.hints.kept_nullifiers, "output nullifiers mismatch"); + } else { + self.validate_sorted_siloed_nullifiers(); + } + + if self.encrypted_log_siloing_amount == 0 { + assert_eq( + self.output.encrypted_log_hashes, self.previous_kernel.end.encrypted_logs_hashes, "output encrypted logs mismatch" + ); + } else { + self.validate_sorted_masked_encrypted_logs(); + } + } + + fn validate_squashed_data(self) { + verify_squashed_transient_data( + self.previous_kernel.end.note_hashes, + self.previous_kernel.end.nullifiers, + self.previous_kernel.end.note_encrypted_logs_hashes, + self.hints.kept_note_hashes, + self.hints.kept_nullifiers, + self.hints.kept_note_encrypted_log_hashes, + self.transient_nullifier_indexes_for_note_hashes, + self.transient_note_hash_indexes_for_nullifiers, + self.hints.transient_or_propagated_note_hash_indexes_for_logs, + self.split_counter + ); + } + + fn validate_sorted_siloed_note_hashes(self) { + // Check that the values are not already siloed in a previous reset. + // Note hashes need to be siloed alltogether because new note hashes added later might affect the ordering and result in wrong nonces. + // We only need to check the first item, since we always start siloing from index 0. + // The first item should either be empty or not siloed (contract_address != 0). + let note_hash = self.previous_kernel.end.note_hashes[0]; + assert( + is_empty(note_hash) | !note_hash.contract_address.is_zero(), "note hashes have been siloed in a previous reset" + ); + + // Don't have to check that note_hash_siloing_amount will cover all the non-empty note hashes. + // If it is not big enough, we won't be able to create a valid tail(_to_public) proof. + // It will fail in previous_kernel_validator > verify_sorted_siloed_values. + + // Check siloing. + let kept_note_hashes = self.hints.kept_note_hashes; + let siloed_note_hashes = self.hints.siloed_note_hashes; + let sorted_hints = self.hints.sorted_note_hash_hints; + let tx_hash = self.output.nullifiers[0].value(); // First nullifier is tx hash. + for i in 0..kept_note_hashes.len() { + let siloed_note_hash = siloed_note_hashes[i]; + let note_hash = kept_note_hashes[i]; + if i < self.note_hash_siloing_amount { + let siloed_value = silo_note_hash(note_hash, tx_hash, sorted_hints[i].sorted_index); + assert_eq(siloed_note_hash.value(), siloed_value, "incorrect siloed note hashes"); + assert_eq( + siloed_note_hash.counter(), note_hash.counter(), "mismatch counter for siloed note hash" + ); + assert( + siloed_note_hash.contract_address.is_zero(), "contract address must not be exposed for siloed note hash" + ); + } else { + // Don't have to check empty items in siloed_note_hash. + // assert_sorted_transformed_value_array ensures that there are the same amount of empty items in kept_note_hashes and in self.output.note_hashes. + // assert(is_empty(siloed_note_hash), "unexpected siloed note hash"); + } + } + + // Check ordering. + assert_sorted_transformed_value_array( + kept_note_hashes, + siloed_note_hashes, + self.output.note_hashes, + sorted_hints + ); + } + + fn validate_sorted_siloed_nullifiers(self) { + // Unlike note hashes, we don't have to check that the nullifiers haven't been siloed. + // silo_nullifier() will return the already-siloed value if contract address is zero. + + // Check siloing. + let kept_nullifiers = self.hints.kept_nullifiers; + let siloed_nullifiers = self.hints.siloed_nullifiers; + let sorted_hints = self.hints.sorted_nullifier_hints; + for i in 0..kept_nullifiers.len() { + let siloed_nullifier = siloed_nullifiers[i]; + let nullifier = kept_nullifiers[i]; + if i < self.nullifier_siloing_amount { + let siloed_value = silo_nullifier(nullifier); + assert_eq(siloed_nullifier.value(), siloed_value, "incorrect siloed nullifier"); + assert_eq(siloed_nullifier.counter(), nullifier.counter(), "mismatch nullifier counter"); + assert_eq( + siloed_nullifier.nullifier.note_hash, nullifier.nullifier.note_hash, "mismatch nullified note hash" + ); + assert( + siloed_nullifier.contract_address.is_zero(), "contract address must not be exposed for siloed nullifier" + ); + } + } + + // Check ordering. + assert_sorted_transformed_value_array( + kept_nullifiers, + siloed_nullifiers, + self.output.nullifiers, + sorted_hints + ); + } + + fn validate_sorted_note_logs(self) { + // This will be called together with validate_sorted_siloed_note_hashes(). + // Same as validate_sorted_siloed_note_hashes, it will only be run once. + // This is fine because we don't allow emitting logs for notes emitted in another function at the moment. + + let kept_log_hashes = self.hints.kept_note_encrypted_log_hashes; + let exposed_log_hashes = self.hints.exposed_note_encrypted_log_hashes; + for i in 0..kept_log_hashes.len() { + let prev = kept_log_hashes[i]; + let out = exposed_log_hashes[i]; + assert_eq(out.value, prev.value, "mismatch note log value"); + assert_eq(out.length, prev.length, "mismatch note log length"); + assert_eq(out.counter, prev.counter, "mismatch note log counter"); + // The note_hash_counter was used when squashing note logs along with their corresponding note hashes. + // It won't be used later on, so we can set them to 0 here. + // It serves as a clue for the tail circuit to check that all the note logs are sorted in a reset circuit. + assert_eq(out.note_hash_counter, 0, "note_hash_counter should not be exposed for sorted note log"); + } + + assert_sorted_transformed_value_array( + kept_log_hashes, + exposed_log_hashes, + self.output.note_encrypted_log_hashes, + self.hints.sorted_note_encrypted_log_hash_hints + ); + } + + fn validate_sorted_masked_encrypted_logs(self) { + // Don't need to check that the logs are already masked. + // If run repeatedly, it will return the masked contract address when randomness becomes 0. + + let log_hashes = self.previous_kernel.end.encrypted_logs_hashes; + let masked_log_hashes = self.hints.masked_encrypted_log_hashes; + for i in 0..log_hashes.len() { + if i < self.encrypted_log_siloing_amount { + let prev = log_hashes[i]; + let out = masked_log_hashes[i]; + assert_eq( + out.contract_address, mask_encrypted_log_hash(prev), "incorrect masked contract address" + ); + assert_eq(out.log_hash.value, prev.log_hash.value, "mismatch log value"); + assert_eq(out.log_hash.length, prev.log_hash.length, "mismatch log length"); + assert_eq(out.log_hash.counter, prev.log_hash.counter, "mismatch log counter"); + assert_eq(out.log_hash.randomness, 0, "randomness must be set to zero after masked"); + } + } + + assert_sorted_transformed_value_array( + log_hashes, + masked_log_hashes, + self.output.encrypted_log_hashes, + self.hints.sorted_encrypted_log_hash_hints + ); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr index a645c56f0fa8..65773bc53573 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr @@ -11,7 +11,6 @@ use dep::types::{ log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, nullifier::ScopedNullifier }, - hash::{compute_tx_logs_hash, compute_tx_note_logs_hash}, messaging::l2_to_l1_message::ScopedL2ToL1Message }; @@ -22,7 +21,7 @@ struct TailOutputComposer { impl TailOutputComposer { pub fn new(previous_kernel: PrivateKernelCircuitPublicInputs) -> Self { let mut output_composer = PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(previous_kernel); - output_composer.sort_and_silo(); + output_composer.sort_ordered_values(); TailOutputComposer { output_composer } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr index c81d74a86230..8580be3f22ea 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr @@ -1,47 +1,37 @@ mod tail_output_hints; -mod validate_value_transformation; use crate::components::{ - previous_kernel_validator::previous_kernel_validator_hints::PreviousKernelValidatorHints, tail_output_composer::meter_gas_used::meter_gas_used, - tail_output_validator::{ - tail_output_hints::{generate_tail_output_hints, TailOutputHints}, - validate_value_transformation::{validate_transformed_values, validate_value_transformation} -} + tail_output_validator::{tail_output_hints::{generate_tail_output_hints, TailOutputHints}} }; use dep::types::{ abis::{ kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs}, log_hash::{LogHash, NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash} }, - messaging::l2_to_l1_message::ScopedL2ToL1Message, - hash::{compute_tx_logs_hash, compute_tx_note_logs_hash, silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, - traits::is_empty, + messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::is_empty, utils::arrays::{assert_sorted_transformed_value_array, assert_sorted_array_with_order_hints} }; struct TailOutputValidator { output: KernelCircuitPublicInputs, previous_kernel: PrivateKernelCircuitPublicInputs, - previous_kernel_hints: PreviousKernelValidatorHints, hints: TailOutputHints, } impl TailOutputValidator { pub fn new( output: KernelCircuitPublicInputs, - previous_kernel: PrivateKernelCircuitPublicInputs, - previous_kernel_hints: PreviousKernelValidatorHints + previous_kernel: PrivateKernelCircuitPublicInputs ) -> Self { let hints = generate_tail_output_hints(previous_kernel); - TailOutputValidator { output, previous_kernel, previous_kernel_hints, hints } + TailOutputValidator { output, previous_kernel, hints } } - pub fn validate(self) { + pub fn validate(self) { self.validate_empty_values(); self.validate_propagated_values(); - self.validate_propagated_sorted_siloed_values(); - self.validate_accumulated_values(); + self.validate_propagated_sorted_values(); self.validate_gas_used(); } @@ -58,44 +48,31 @@ impl TailOutputValidator { ); assert_eq(self.output.fee_payer, self.previous_kernel.fee_payer, "mismatch fee_payer"); - } - fn validate_propagated_sorted_siloed_values(self) { // note_hashes - let siloed_note_hashes = self.previous_kernel_hints.siloed_note_hashes; - let sorted_note_hash_hints = self.hints.sorted_note_hash_hints; - let unsiloed_note_hashes = self.previous_kernel.end.note_hashes; - let tx_hash = self.output.end.nullifiers[0]; // First nullifier is tx hash. - for i in 0..unsiloed_note_hashes.len() { - let siloed_note_hash = silo_note_hash( - unsiloed_note_hashes[i], - tx_hash, - sorted_note_hash_hints[i].sorted_index - ); - assert_eq(siloed_note_hashes[i], siloed_note_hash, "mismatch siloed note hashes"); + let note_hashes = self.previous_kernel.end.note_hashes; + for i in 0..note_hashes.len() { + assert_eq(note_hashes[i].value(), self.output.end.note_hashes[i], "mismatch note_hashes"); } - assert_sorted_transformed_value_array( - self.previous_kernel.end.note_hashes, - siloed_note_hashes, - self.output.end.note_hashes, - sorted_note_hash_hints - ); - // nullifiers - validate_transformed_values( - self.previous_kernel.end.nullifiers, - self.hints.siloed_nullifiers, - silo_nullifier + let nullifiers = self.previous_kernel.end.nullifiers; + for i in 0..nullifiers.len() { + assert_eq(nullifiers[i].value(), self.output.end.nullifiers[i], "mismatch nullifiers"); + } + + // note_encrypted_logs_hashes + assert_eq( + self.previous_kernel.end.note_encrypted_logs_hashes.map(|log: NoteLogHash| log.expose_to_public()), self.output.end.note_encrypted_logs_hashes, "mismatch note_encrypted_logs_hashes" ); - assert_sorted_transformed_value_array( - self.previous_kernel.end.nullifiers, - self.hints.siloed_nullifiers, - self.output.end.nullifiers, - self.hints.sorted_nullifier_hints + // encrypted_logs_hashes + assert_eq( + self.previous_kernel.end.encrypted_logs_hashes.map(|log: ScopedEncryptedLogHash| log.expose_to_public()), self.output.end.encrypted_logs_hashes, "mismatch encrypted_logs_hashes" ); + } + fn validate_propagated_sorted_values(self) { // l2_to_l1_msgs assert_sorted_transformed_value_array( self.previous_kernel.end.l2_to_l1_msgs, @@ -103,37 +80,6 @@ impl TailOutputValidator { self.output.end.l2_to_l1_msgs, self.hints.sorted_l2_to_l1_msg_hints ); - } - - fn validate_accumulated_values(self) { - // note_encrypted_log_hashes - assert_sorted_array_with_order_hints( - self.previous_kernel.end.note_encrypted_logs_hashes, - self.hints.sorted_note_encrypted_log_hashes, - self.hints.sorted_note_encrypted_log_hash_hints - ); - - assert_eq( - self.hints.sorted_note_encrypted_log_hashes.map(|log: NoteLogHash| log.expose_to_public()), self.output.end.note_encrypted_logs_hashes, "mismatch note_encrypted_logs_hashes" - ); - - // encrypted_log_hashes - validate_value_transformation( - self.previous_kernel.end.encrypted_logs_hashes, - self.hints.masked_encrypted_log_hashes, - |lh: ScopedEncryptedLogHash, mlh: ScopedLogHash| - (mlh.contract_address == mask_encrypted_log_hash(lh)) & - (lh.log_hash.value == mlh.log_hash.value) & - (lh.log_hash.length == mlh.log_hash.length) & - (mlh.log_hash.counter == 0) - ); - - assert_sorted_transformed_value_array( - self.previous_kernel.end.encrypted_logs_hashes, - self.hints.masked_encrypted_log_hashes, - self.output.end.encrypted_logs_hashes, - self.hints.sorted_masked_encrypted_log_hash_hints - ); // unencrypted_log_hashes assert_sorted_array_with_order_hints( diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/tail_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/tail_output_hints.nr index 32736bb75356..30e881a36652 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/tail_output_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/tail_output_hints.nr @@ -1,73 +1,24 @@ use dep::types::{ - abis::{ - kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - log_hash::{LogHash, NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash} -}, - constants::{ - MAX_ENCRYPTED_LOGS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX -}, - hash::{silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, - messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::{Empty, is_empty}, + abis::{kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::ScopedLogHash}, + constants::{MAX_L2_TO_L1_MSGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX}, utils::arrays::{OrderHint, sort_by_counters_asc, sort_get_order_hints_asc} }; struct TailOutputHints { - // Note hashes. - sorted_note_hash_hints: [OrderHint; MAX_NOTE_HASHES_PER_TX], - // Nullifiers. - sorted_nullifier_hints: [OrderHint; MAX_NULLIFIERS_PER_TX], - siloed_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], // L2 to l1 msgs. sorted_l2_to_l1_msg_hints: [OrderHint; MAX_L2_TO_L1_MSGS_PER_TX], - // Note encrypted log hashes. - sorted_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hash_hints: [OrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - // Encrypted log hashes. - masked_encrypted_log_hashes: [ScopedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_masked_encrypted_log_hash_hints: [OrderHint; MAX_ENCRYPTED_LOGS_PER_TX], // Unencrypted log hashes. sorted_unencrypted_log_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hash_hints: [OrderHint; MAX_UNENCRYPTED_LOGS_PER_TX], } unconstrained pub fn generate_tail_output_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> TailOutputHints { - // note_hashes - let sorted_note_hash_hints = sort_get_order_hints_asc(previous_kernel.end.note_hashes); - - // nullifiers - let sorted_nullifier_hints = sort_get_order_hints_asc(previous_kernel.end.nullifiers); - let siloed_nullifiers = previous_kernel.end.nullifiers.map(silo_nullifier); - // l2_to_l1_msgs let sorted_l2_to_l1_msg_hints = sort_get_order_hints_asc(previous_kernel.end.l2_to_l1_msgs); - // note_encrypted_logs - let sorted_note_encrypted_log_hashes = sort_by_counters_asc(previous_kernel.end.note_encrypted_logs_hashes); - let sorted_note_encrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.note_encrypted_logs_hashes); - - // encrypted_logs - let mut masked_log_hashes = previous_kernel.end.encrypted_logs_hashes; - for i in 0..masked_log_hashes.len() { - masked_log_hashes[i].contract_address = mask_encrypted_log_hash(previous_kernel.end.encrypted_logs_hashes[i]); - } - let masked_encrypted_log_hashes = masked_log_hashes.map(|h: ScopedEncryptedLogHash| h.expose_to_public()); - let sorted_masked_encrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.encrypted_logs_hashes); - // unencrypted_logs let sorted_unencrypted_log_hashes = sort_by_counters_asc(previous_kernel.end.unencrypted_logs_hashes); let sorted_unencrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.unencrypted_logs_hashes); - TailOutputHints { - sorted_note_hash_hints, - sorted_nullifier_hints, - siloed_nullifiers, - sorted_l2_to_l1_msg_hints, - sorted_note_encrypted_log_hashes, - sorted_note_encrypted_log_hash_hints, - masked_encrypted_log_hashes, - sorted_masked_encrypted_log_hash_hints, - sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hash_hints - } + TailOutputHints { sorted_l2_to_l1_msg_hints, sorted_unencrypted_log_hashes, sorted_unencrypted_log_hash_hints } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_value_transformation.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_value_transformation.nr deleted file mode 100644 index e7b8d392702d..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_value_transformation.nr +++ /dev/null @@ -1,23 +0,0 @@ -use dep::types::traits::Empty; - -pub fn validate_value_transformation( - original_array: [T; N], - transformed_value_array: [S; N], - is_transformed: fn[Env](T, S) -> bool -) { - for i in 0..N { - assert(is_transformed(original_array[i], transformed_value_array[i]), "invalid transformed value"); - } -} - -pub fn validate_transformed_values( - original_array: [T; N], - transformed_value_array: [S; N], - transform_value: fn[Env](T) -> S -) where S: Empty + Eq { - validate_value_transformation( - original_array, - transformed_value_array, - |original: T, transformed: S| transformed == transform_value(original) - ); -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr index 4a22e8d18d51..4b4784de94b5 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr @@ -20,7 +20,7 @@ struct TailToPublicOutputComposer { impl TailToPublicOutputComposer { pub fn new(previous_kernel: PrivateKernelCircuitPublicInputs) -> Self { let mut output_composer = PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(previous_kernel); - output_composer.sort_and_silo(); + output_composer.sort_ordered_values(); TailToPublicOutputComposer { output_composer } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr index 7cd33f5aac10..6cef6663a9b3 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr @@ -1,47 +1,42 @@ mod tail_to_public_output_hints; use crate::components::{ - previous_kernel_validator::previous_kernel_validator_hints::PreviousKernelValidatorHints, - tail_output_validator::{validate_value_transformation::{validate_transformed_values, validate_value_transformation}}, tail_to_public_output_composer::meter_gas_used::{meter_gas_used_non_revertible, meter_gas_used_revertible}, tail_to_public_output_validator::tail_to_public_output_hints::{generate_tail_to_public_output_hints, TailToPublicOutputHints} }; use dep::types::{ abis::{ kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, - log_hash::{LogHash, ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, note_hash::NoteHash, - nullifier::Nullifier, public_call_request::PublicCallRequest + log_hash::{LogHash, ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, + nullifier::{Nullifier, ScopedNullifier}, public_call_request::PublicCallRequest }, - messaging::l2_to_l1_message::ScopedL2ToL1Message, address::AztecAddress, - hash::{silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, - traits::{Empty, is_empty, is_empty_array}, + messaging::l2_to_l1_message::ScopedL2ToL1Message, address::AztecAddress, traits::is_empty_array, utils::arrays::{ - array_length, assert_split_sorted_transformed_value_arrays_asc, - assert_split_sorted_transformed_value_arrays_desc, validate_array + assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc, + assert_split_transformed_value_arrays, validate_array } }; struct TailToPublicOutputValidator { output: PublicKernelCircuitPublicInputs, previous_kernel: PrivateKernelCircuitPublicInputs, - previous_kernel_hints: PreviousKernelValidatorHints, hints: TailToPublicOutputHints, } impl TailToPublicOutputValidator { pub fn new( output: PublicKernelCircuitPublicInputs, - previous_kernel: PrivateKernelCircuitPublicInputs, - previous_kernel_hints: PreviousKernelValidatorHints + previous_kernel: PrivateKernelCircuitPublicInputs ) -> Self { let hints = generate_tail_to_public_output_hints(previous_kernel); - TailToPublicOutputValidator { output, previous_kernel, previous_kernel_hints, hints } + TailToPublicOutputValidator { output, previous_kernel, hints } } - pub fn validate(self) { + pub fn validate(self) { self.validate_empty_values(); self.validate_propagated_values(); - self.validate_propagated_sorted_siloed_values(); + self.validate_propagated_split_values(); + self.validate_propagated_sorted_values(); self.validate_gas_used(); } @@ -54,6 +49,14 @@ impl TailToPublicOutputValidator { assert( is_empty_array(self.output.end.public_data_update_requests), "revertible public_data_update_requests must be empty" ); + + // public_data_update_requests + assert( + is_empty_array(self.output.end_non_revertible.public_data_update_requests), "unexpected non-revertible public_data_update_requests" + ); + assert( + is_empty_array(self.output.end.public_data_update_requests), "unexpected revertible public_data_update_requests" + ); } fn validate_propagated_values(self) { @@ -78,100 +81,64 @@ impl TailToPublicOutputValidator { ); } - fn validate_propagated_sorted_siloed_values(self) { + fn validate_propagated_split_values(self) { let split_counter = self.previous_kernel.min_revertible_side_effect_counter; let prev_data = self.previous_kernel.end; let output_non_revertible = self.output.end_non_revertible; let output_revertible = self.output.end; - let hints = self.hints; - let previous_kernel_hints = self.previous_kernel_hints; // note_hashes - let siloed_note_hashes = previous_kernel_hints.siloed_note_hashes; - let unsiloed_note_hashes = prev_data.note_hashes; - let tx_hash = output_non_revertible.nullifiers[0].value; - let sorted_split_indexes = hints.sorted_note_hash_hints.sorted_indexes; - let sorted_counters_gte = hints.sorted_note_hash_hints.sorted_counters_gte; - let num_non_revertible = array_length(output_non_revertible.note_hashes); - for i in 0..unsiloed_note_hashes.len() { - let note_hash = unsiloed_note_hashes[i]; - let mut index = sorted_split_indexes[i]; - if sorted_counters_gte[index] == note_hash.counter() { - index += num_non_revertible; - } - let siloed_note_hash = silo_note_hash(note_hash, tx_hash, index); - assert_eq(siloed_note_hashes[i], siloed_note_hash, "mismatch siloed note hashes"); - } - - assert_split_sorted_transformed_value_arrays_asc( + assert_split_transformed_value_arrays( prev_data.note_hashes, - siloed_note_hashes.map(|value: Field| NoteHash { value, counter: 0 }.scope(AztecAddress::zero())), - split_counter, output_non_revertible.note_hashes, output_revertible.note_hashes, - hints.sorted_note_hash_hints + |prev: ScopedNoteHash, out: ScopedNoteHash| out == prev.expose_to_public(), + split_counter ); // nullifiers - validate_value_transformation( + assert_split_transformed_value_arrays( prev_data.nullifiers, - hints.siloed_nullifiers, - |sn, n: Nullifier| (n.value == silo_nullifier(sn)) & (n.counter == 0) & (n.note_hash == 0) - ); - assert_split_sorted_transformed_value_arrays_asc( - prev_data.nullifiers, - hints.siloed_nullifiers, - split_counter, output_non_revertible.nullifiers, output_revertible.nullifiers, - hints.sorted_nullifier_hints - ); - - // l2_to_l1_msgs - - assert_split_sorted_transformed_value_arrays_asc( - prev_data.l2_to_l1_msgs, - prev_data.l2_to_l1_msgs.map(|log: ScopedL2ToL1Message| log.expose_to_public()), - split_counter, - output_non_revertible.l2_to_l1_msgs, - output_revertible.l2_to_l1_msgs, - hints.sorted_l2_to_l1_msg_hints + |prev: ScopedNullifier, out: Nullifier| out == prev.expose_to_public(), + split_counter ); // note_encrypted_logs_hashes - validate_value_transformation( + assert_split_transformed_value_arrays( prev_data.note_encrypted_logs_hashes, - hints.note_encrypted_logs_hashes, - |nlh: NoteLogHash, lh: LogHash| (lh.value == nlh.value) & (lh.length == nlh.length) & (lh.counter == 0) - ); - - assert_split_sorted_transformed_value_arrays_asc( - prev_data.note_encrypted_logs_hashes, - hints.note_encrypted_logs_hashes, - split_counter, output_non_revertible.note_encrypted_logs_hashes, output_revertible.note_encrypted_logs_hashes, - hints.sorted_note_encrypted_log_hash_hints + |prev: NoteLogHash, out: LogHash| out == prev.expose_to_public(), + split_counter ); // encrypted_logs_hashes - validate_value_transformation( + assert_split_transformed_value_arrays( prev_data.encrypted_logs_hashes, - hints.masked_encrypted_logs_hashes, - |lh: ScopedEncryptedLogHash, mlh: ScopedLogHash| - (mlh.contract_address == mask_encrypted_log_hash(lh)) & - (lh.log_hash.value == mlh.log_hash.value) & - (lh.log_hash.length == mlh.log_hash.length) & - (mlh.log_hash.counter == 0) + output_non_revertible.encrypted_logs_hashes, + output_revertible.encrypted_logs_hashes, + |prev: ScopedEncryptedLogHash, out: ScopedLogHash| out == prev.expose_to_public(), + split_counter ); + } + fn validate_propagated_sorted_values(self) { + let split_counter = self.previous_kernel.min_revertible_side_effect_counter; + let prev_data = self.previous_kernel.end; + let output_non_revertible = self.output.end_non_revertible; + let output_revertible = self.output.end; + let hints = self.hints; + + // l2_to_l1_msgs assert_split_sorted_transformed_value_arrays_asc( - prev_data.encrypted_logs_hashes, - hints.masked_encrypted_logs_hashes, + prev_data.l2_to_l1_msgs, + prev_data.l2_to_l1_msgs.map(|log: ScopedL2ToL1Message| log.expose_to_public()), split_counter, - output_non_revertible.encrypted_logs_hashes, - output_revertible.encrypted_logs_hashes, - hints.sorted_encrypted_log_hash_hints + output_non_revertible.l2_to_l1_msgs, + output_revertible.l2_to_l1_msgs, + hints.sorted_l2_to_l1_msg_hints ); // unencrypted_logs_hashes @@ -184,23 +151,10 @@ impl TailToPublicOutputValidator { hints.sorted_unencrypted_log_hash_hints ); - // public_data_update_requests - let num_non_revertible_writes = validate_array(output_non_revertible.public_data_update_requests); - assert_eq(num_non_revertible_writes, 0, "unexpected non-revertible public_data_update_requests"); - let num_revertible_writes = validate_array(output_revertible.public_data_update_requests); - assert_eq(num_revertible_writes, 0, "unexpected revertible public_data_update_requests"); - // public_call_requests - validate_value_transformation( - prev_data.public_call_requests, - hints.public_call_requests, - |cr: PublicCallRequest, public_cr: PublicCallRequest| (public_cr.item == cr.item) - & (public_cr.counter == 0) - ); - assert_split_sorted_transformed_value_arrays_desc( prev_data.public_call_requests, - hints.public_call_requests, + prev_data.public_call_requests.map(|cr: PublicCallRequest| cr.expose_to_public()), split_counter, output_non_revertible.public_call_stack, output_revertible.public_call_stack, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator/tail_to_public_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator/tail_to_public_output_hints.nr index e4248176275f..c43687a33039 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator/tail_to_public_output_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator/tail_to_public_output_hints.nr @@ -1,90 +1,29 @@ use dep::types::{ - abis::{ - kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - log_hash::{LogHash, NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash}, note_hash::NoteHash, - nullifier::Nullifier, public_call_request::PublicCallRequest -}, - constants::{ - MAX_ENCRYPTED_LOGS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX -}, - hash::{silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, - messaging::l2_to_l1_message::ScopedL2ToL1Message, + abis::{kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs}, + constants::{MAX_L2_TO_L1_MSGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX}, utils::arrays::{sort_get_split_order_hints_asc, sort_get_split_order_hints_desc, SplitOrderHints} }; struct TailToPublicOutputHints { - // Note hashes. - sorted_note_hash_hints: SplitOrderHints, - // Nullifiers. - siloed_nullifiers: [Nullifier; MAX_NULLIFIERS_PER_TX], - sorted_nullifier_hints: SplitOrderHints, // L2 to l1 msgs. sorted_l2_to_l1_msg_hints: SplitOrderHints, - // Note encrypted log hashes. - note_encrypted_logs_hashes: [LogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hash_hints: SplitOrderHints, - // Encrypted log hashes. - masked_encrypted_logs_hashes: [ScopedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hash_hints: SplitOrderHints, // Unencrypted log hashes. sorted_unencrypted_log_hash_hints: SplitOrderHints, // Public call requests. - public_call_requests: [PublicCallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], sorted_public_call_request_hints: SplitOrderHints, } unconstrained pub fn generate_tail_to_public_output_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> TailToPublicOutputHints { let split_counter = previous_kernel.min_revertible_side_effect_counter; - // note_hashes - let unsiloed_note_hashes = previous_kernel.end.note_hashes; - let sorted_note_hash_hints = sort_get_split_order_hints_asc(unsiloed_note_hashes, split_counter); - - // nullifiers - let unsiloed_nullifiers = previous_kernel.end.nullifiers; - - let mut siloed_nullifiers = [Nullifier::empty(); MAX_NULLIFIERS_PER_TX]; - for i in 0..unsiloed_nullifiers.len() { - siloed_nullifiers[i].value = silo_nullifier(unsiloed_nullifiers[i]); - } - - let sorted_nullifier_hints = sort_get_split_order_hints_asc(unsiloed_nullifiers, split_counter); - // l2_to_l1_msgss let sorted_l2_to_l1_msg_hints = sort_get_split_order_hints_asc(previous_kernel.end.l2_to_l1_msgs, split_counter); - // note_encrypted_logs - let note_encrypted_logs_hashes = previous_kernel.end.note_encrypted_logs_hashes.map(|h: NoteLogHash| h.expose_to_public()); - let sorted_note_encrypted_log_hash_hints = sort_get_split_order_hints_asc(previous_kernel.end.note_encrypted_logs_hashes, split_counter); - - // encrypted_logs - let masked_encrypted_logs_hashes = previous_kernel.end.encrypted_logs_hashes.map( - |mut h: ScopedEncryptedLogHash| { - h.contract_address = mask_encrypted_log_hash(h); - h.expose_to_public() - } - ); - let sorted_encrypted_log_hash_hints = sort_get_split_order_hints_asc(previous_kernel.end.encrypted_logs_hashes, split_counter); - // unencrypted_logs let sorted_unencrypted_log_hash_hints = sort_get_split_order_hints_asc(previous_kernel.end.unencrypted_logs_hashes, split_counter); // public_call_requests - let public_call_requests = previous_kernel.end.public_call_requests.map(|cr: PublicCallRequest| cr.expose_to_public()); let sorted_public_call_request_hints = sort_get_split_order_hints_desc(previous_kernel.end.public_call_requests, split_counter); - TailToPublicOutputHints { - sorted_note_hash_hints, - sorted_nullifier_hints, - siloed_nullifiers, - sorted_l2_to_l1_msg_hints, - note_encrypted_logs_hashes, - sorted_note_encrypted_log_hash_hints, - masked_encrypted_logs_hashes, - sorted_encrypted_log_hash_hints, - sorted_unencrypted_log_hash_hints, - public_call_requests, - sorted_public_call_request_hints - } + TailToPublicOutputHints { sorted_l2_to_l1_msg_hints, sorted_unencrypted_log_hash_hints, sorted_public_call_request_hints } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr index 5b0e4df71a0e..9ba7c2bb7626 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -1,7 +1,10 @@ -use crate::components::reset_output_composer::{PrivateKernelResetOutputs, ResetOutputComposer}; +use crate::components::{ + reset_output_composer::{PrivateKernelResetOutputs, ResetOutputComposer, ResetOutputHints}, + reset_output_validator::ResetOutputValidator +}; use dep::reset_kernel_lib::{ - get_transient_or_propagated_note_hash_indexes_for_logs, KeyValidationHint, NoteHashReadRequestHints, - NullifierReadRequestHints, PrivateValidationRequestProcessor, verify_squashed_transient_data + KeyValidationHint, NoteHashReadRequestHints, NullifierReadRequestHints, + PrivateValidationRequestProcessor }; use dep::types::{ abis::private_kernel_data::PrivateKernelData, @@ -29,17 +32,35 @@ struct PrivateKernelResetCircuitPrivateInputs PrivateKernelResetCircuitPrivateInputs { - unconstrained fn generate_output(self) -> PrivateKernelResetOutputs { - ResetOutputComposer::new( + unconstrained fn generate_output( + self, + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32 + ) -> (PrivateKernelResetOutputs, ResetOutputHints) { + let composer = ResetOutputComposer::new( self.previous_kernel.public_inputs, self.hints.transient_nullifier_indexes_for_note_hashes, - self.hints.transient_note_hash_indexes_for_nullifiers - ).finish() + self.hints.transient_note_hash_indexes_for_nullifiers, + note_hash_siloing_amount, + nullifier_siloing_amount, + encrypted_log_siloing_amount + ); + (composer.finish(), composer.hints) } - pub fn execute(self) -> PrivateKernelCircuitPublicInputs { + pub fn execute( + self, + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32 + ) -> PrivateKernelCircuitPublicInputs { // Generate output. - let output = self.generate_output(); + let (output, output_hints) = self.generate_output( + note_hash_siloing_amount, + nullifier_siloing_amount, + encrypted_log_siloing_amount + ); // Validate inputs. if !std::runtime::is_unconstrained() { @@ -62,23 +83,17 @@ impl Self { let mut previous_kernel = FixtureBuilder::new().in_vk_tree(PRIVATE_KERNEL_INNER_INDEX); - previous_kernel.append_nullifiers(1); + previous_kernel.set_first_nullifier(); PrivateKernelResetInputsBuilder { previous_kernel, @@ -140,10 +161,20 @@ mod tests { transient_note_hash_indexes_for_nullifiers: [MAX_NOTE_HASHES_PER_TX; MAX_NULLIFIERS_PER_TX], note_hash_read_request_hints_builder: NoteHashReadRequestHintsBuilder::new(MAX_NOTE_HASH_READ_REQUESTS_PER_TX), nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(MAX_NULLIFIER_READ_REQUESTS_PER_TX), - validation_requests_split_counter: 0 + validation_requests_split_counter: 0, + note_hash_siloing_amount: 0, + nullifier_siloing_amount: 0, + encrypted_log_siloing_amount: 0 } } + pub fn with_siloing(&mut self) -> Self { + self.note_hash_siloing_amount = 6; + self.nullifier_siloing_amount = 6; + self.encrypted_log_siloing_amount = 4; + *self + } + pub fn add_pending_note_hash_read_request(&mut self, note_hash_index: u32) { let read_request_index = self.previous_kernel.add_read_request_for_pending_note_hash(note_hash_index); let hint_index = self.note_hash_read_request_hints_builder.pending_read_hints.len(); @@ -168,6 +199,47 @@ mod tests { self.transient_note_hash_indexes_for_nullifiers[nullifier_index] = note_hash_index; } + pub fn compute_output_note_hashes(self, note_hashes: [ScopedNoteHash; N]) -> [ScopedNoteHash; N] { + // First nullifier is tx hash. + let tx_hash = self.previous_kernel.nullifiers.get_unchecked(0).value(); + let mut output = note_hashes; + for i in 0..N { + output[i].note_hash.value = silo_note_hash(note_hashes[i], tx_hash, i); + output[i].contract_address = AztecAddress::zero(); + } + output + } + + pub fn compute_output_nullifiers(_self: Self, nullifiers: [ScopedNullifier; N]) -> [ScopedNullifier; N] { + let mut output = nullifiers; + for i in 1..N { + output[i].nullifier.value = silo_nullifier(nullifiers[i]); + output[i].nullifier.note_hash = 0; + output[i].contract_address = AztecAddress::zero(); + } + output + } + + pub fn compute_output_note_logs(_self: Self, logs: [NoteLogHash; N]) -> [NoteLogHash; N] { + let mut output = logs; + for i in 0..N { + output[i].note_hash_counter = 0; + } + output + } + + pub fn compute_output_encrypted_logs( + _self: Self, + logs: [ScopedEncryptedLogHash; N] + ) -> [ScopedEncryptedLogHash; N] { + let mut output = logs; + for i in 0..N { + output[i].contract_address = mask_encrypted_log_hash(output[i]); + output[i].log_hash.randomness = 0; + } + output + } + pub fn execute(&mut self) -> PrivateKernelCircuitPublicInputs { let hints = PrivateKernelResetHints { transient_nullifier_indexes_for_note_hashes: self.transient_nullifier_indexes_for_note_hashes, @@ -179,7 +251,11 @@ mod tests { }; let kernel = PrivateKernelResetCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), hints }; - kernel.execute() + kernel.execute( + self.note_hash_siloing_amount, + self.nullifier_siloing_amount, + self.encrypted_log_siloing_amount + ) } pub fn failed(&mut self) { @@ -454,6 +530,84 @@ mod tests { let _res = builder.execute(); } } + + #[test] + fn squashing_and_siloing_and_ordering_succeeds() { + let mut builder = PrivateKernelResetInputsBuilder::new().with_siloing(); + + builder.previous_kernel.append_note_hashes_with_logs(4); + builder.previous_kernel.append_nullifiers(3); + builder.previous_kernel.append_encrypted_log_hashes(3); + // Get ordered items before shuffling. + let note_hashes = builder.previous_kernel.note_hashes.storage; + let nullifiers = builder.previous_kernel.nullifiers.storage; + let note_logs = builder.previous_kernel.note_encrypted_logs_hashes.storage; + let encrypted_logs = builder.previous_kernel.encrypted_logs_hashes.storage; + // Shuffle. + swap_items(&mut builder.previous_kernel.note_hashes, 1, 0); + swap_items(&mut builder.previous_kernel.note_hashes, 3, 2); + swap_items(&mut builder.previous_kernel.nullifiers, 2, 3); + swap_items(&mut builder.previous_kernel.note_encrypted_logs_hashes, 1, 3); + swap_items(&mut builder.previous_kernel.encrypted_logs_hashes, 1, 2); + // The nullifier at index 1 is nullifying the note hash at index 3 (original index 2). + builder.nullify_pending_note_hash(1, 3); + + let public_inputs = builder.execute(); + + // The note hash at index 2 is chopped. + let output_note_hashes = builder.compute_output_note_hashes([note_hashes[0], note_hashes[1], note_hashes[3]]); + assert(array_eq(public_inputs.end.note_hashes, output_note_hashes)); + + // The nullifier at index 1 is chopped. + let output_nullifiers = builder.compute_output_nullifiers([nullifiers[0], nullifiers[2], nullifiers[3]]); + assert(array_eq(public_inputs.end.nullifiers, output_nullifiers)); + + // The note log at index 2 is chopped. + let output_note_logs = builder.compute_output_note_logs([note_logs[0], note_logs[1], note_logs[3]]); + assert( + array_eq( + public_inputs.end.note_encrypted_logs_hashes, + output_note_logs + ) + ); + + let output_logs = builder.compute_output_encrypted_logs([encrypted_logs[0], encrypted_logs[1], encrypted_logs[2]]); + assert(array_eq(public_inputs.end.encrypted_logs_hashes, output_logs)); + } + + #[test(should_fail_with="note hashes have been siloed in a previous reset")] + fn siloing_note_hashes_again_fails() { + let mut builder = PrivateKernelResetInputsBuilder::new().with_siloing(); + + builder.previous_kernel.append_note_hashes_with_logs(2); + + // The note hash at index 0 is siloed. + builder.previous_kernel.note_hashes.storage[0].contract_address = AztecAddress::zero(); + + builder.failed(); + } + + #[test] + fn siloing_nullifiers_again_succeeds() { + let mut builder = PrivateKernelResetInputsBuilder::new().with_siloing(); + + builder.previous_kernel.append_nullifiers(2); + + // The nullifier at index 1 is siloed. + builder.previous_kernel.nullifiers.storage[1].contract_address = AztecAddress::zero(); + + let nullifiers = builder.previous_kernel.nullifiers.storage; + + let public_inputs = builder.execute(); + + let output_nullifiers = public_inputs.end.nullifiers; + // The 0th nullifier (tx hash) doesn't change. + assert(output_nullifiers[0].value() == nullifiers[0].value()); + // The 1st nullifier doesn't change. + assert(output_nullifiers[1].value() == nullifiers[1].value()); + // The 2nd nullifier has been siloed + assert(output_nullifiers[2].value() != nullifiers[2].value()); + } // TODO(#7410) we need the tube vk to reinstate this // #[test(should_fail_with="Invalid vk index")] // fn invalid_previous_kernel() { 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 a52abc72a811..6463f8e4f856 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 @@ -35,8 +35,7 @@ impl PrivateKernelTailCircuitPrivateInputs { let output = self.generate_output(); // Validate inputs. - let previous_kernel_validator = PreviousKernelValidator::new(self.previous_kernel.public_inputs); - previous_kernel_validator.validate_for_private_tail(); + PreviousKernelValidator::new(self.previous_kernel.public_inputs).validate_for_private_tail(); if !std::runtime::is_unconstrained() { // TODO(#7410) currently stubbed out until tube vk handled // self.previous_kernel.validate_in_vk_tree(ALLOWED_PREVIOUS_CIRCUITS); @@ -44,11 +43,7 @@ impl PrivateKernelTailCircuitPrivateInputs { // Validate output. if dep::types::validate::should_validate_output() { - TailOutputValidator::new( - output, - self.previous_kernel.public_inputs, - previous_kernel_validator.hints - ).validate(); + TailOutputValidator::new(output, self.previous_kernel.public_inputs).validate(); } output @@ -58,20 +53,16 @@ impl PrivateKernelTailCircuitPrivateInputs { mod tests { use crate::private_kernel_tail::{PrivateKernelTailCircuitPrivateInputs, ALLOWED_PREVIOUS_CIRCUITS}; use dep::types::constants::{ - MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, - GENERATOR_INDEX__IVSK_M, L2_GAS_PER_LOG_BYTE, L2_GAS_PER_NULLIFIER, L2_GAS_PER_NOTE_HASH, - PRIVATE_KERNEL_INNER_INDEX, BASE_ROLLUP_INDEX + DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, GENERATOR_INDEX__IVSK_M, L2_GAS_PER_LOG_BYTE, + L2_GAS_PER_NULLIFIER, L2_GAS_PER_NOTE_HASH, PRIVATE_KERNEL_INNER_INDEX, BASE_ROLLUP_INDEX }; use dep::types::{ abis::{ kernel_circuit_public_inputs::KernelCircuitPublicInputs, max_block_number::MaxBlockNumber, - note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, gas::Gas, - log_hash::{ScopedLogHash, ScopedEncryptedLogHash, LogHash, NoteLogHash} + gas::Gas, log_hash::ScopedLogHash }, - address::{AztecAddress, EthAddress}, scalar::Scalar, - hash::{sha256_to_field, silo_note_hash, silo_nullifier, mask_encrypted_log_hash}, - tests::fixture_builder::FixtureBuilder, utils::{arrays::array_length}, - traits::{Empty, is_empty}, point::Point + address::{AztecAddress, EthAddress}, tests::fixture_builder::FixtureBuilder, traits::is_empty, + point::Point }; // TODO: Reduce the duplicated code/tests for PrivateKernelTailInputs and PrivateKernelTailToPublicInputs. @@ -88,27 +79,6 @@ mod tests { PrivateKernelTailInputsBuilder { previous_kernel } } - // 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_output_note_hashes(self, note_hashes: [ScopedNoteHash; N]) -> [Field; N] { - // First nullifier is tx hash. - let tx_hash = self.previous_kernel.nullifiers.get_unchecked(0).value(); - let mut output = [0; N]; - for i in 0..N { - output[i] = silo_note_hash(note_hashes[i], tx_hash, i); - } - output - } - - pub fn compute_output_nullifiers(_self: Self, nullifiers: [ScopedNullifier; N]) -> [Field; N] { - let mut output = [0; N]; - output[0] = nullifiers[0].value(); - for i in 1..N { - output[i] = silo_nullifier(nullifiers[i]); - } - output - } - pub fn execute(&mut self) -> KernelCircuitPublicInputs { let kernel = PrivateKernelTailCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data() }; kernel.execute() @@ -148,7 +118,7 @@ mod tests { let prev_encrypted_log_preimages_length = 13; let prev_unencrypted_logs_hash = 956; let prev_unencrypted_log_preimages_length = 24; - builder.previous_kernel.add_encrypted_log_hash(prev_encrypted_logs_hash, prev_encrypted_log_preimages_length); + builder.previous_kernel.add_masked_encrypted_log_hash(prev_encrypted_logs_hash, prev_encrypted_log_preimages_length); builder.previous_kernel.add_unencrypted_log_hash( prev_unencrypted_logs_hash, prev_unencrypted_log_preimages_length @@ -166,68 +136,6 @@ mod tests { ); } - #[test] - fn ordering_of_encrypted_logs() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_encrypted_log_hashes(2); - - // Set the randomness in the first log to 0 to signal not masking the address to silo with - builder.previous_kernel.encrypted_logs_hashes.storage[0].log_hash.randomness = 0; - - // Reorder the logs - let mut reversed_logs = [ScopedEncryptedLogHash::empty(); 2]; - for i in 0..reversed_logs.len() { - reversed_logs[i] = builder.previous_kernel.encrypted_logs_hashes.pop(); - } - builder.previous_kernel.encrypted_logs_hashes.extend_from_array(reversed_logs); - - let public_inputs = builder.execute(); - - let resulting_encrypted_logs = public_inputs.end.encrypted_logs_hashes; - - assert_eq( - resulting_encrypted_logs[0], ScopedLogHash { - contract_address: builder.previous_kernel.storage_contract_address, // Not masked, randomness = 0 - log_hash: LogHash { value: reversed_logs[1].log_hash.value, counter: 0, length: reversed_logs[1].log_hash.length } - } - ); - - assert_eq( - resulting_encrypted_logs[1], ScopedLogHash { - contract_address: mask_encrypted_log_hash(reversed_logs[0]), - log_hash: LogHash { value: reversed_logs[0].log_hash.value, counter: 0, length: reversed_logs[0].log_hash.length } - } - ); - } - - #[test] - fn ordering_of_note_encrypted_logs() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_note_encrypted_log_hashes(2); - - // Reorder the logs - let original_logs = builder.previous_kernel.note_encrypted_logs_hashes.storage; - let mut reversed_logs = [NoteLogHash::empty(); 2]; - for i in 0..reversed_logs.len() { - reversed_logs[i] = builder.previous_kernel.note_encrypted_logs_hashes.pop(); - } - builder.previous_kernel.note_encrypted_logs_hashes.extend_from_array(reversed_logs); - - let public_inputs = builder.execute(); - - let resulting_encrypted_logs = public_inputs.end.note_encrypted_logs_hashes; - - assert_eq( - resulting_encrypted_logs, original_logs.map( - |log: NoteLogHash| { - LogHash { value: log.value, counter: 0, length: log.length } - } - ) - ); - } - #[test] fn ordering_of_unencrypted_logs() { let mut builder = PrivateKernelTailInputsBuilder::new(); @@ -256,57 +164,6 @@ mod tests { ); } - #[test] - fn ordering_of_note_hashes_and_nullifiers() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_note_hashes(10); - builder.previous_kernel.append_nullifiers(10); - - let sorted_note_hashes = builder.previous_kernel.note_hashes.storage; - let sorted_nullifiers = builder.previous_kernel.nullifiers.storage; - - let mut reversed_note_hashes = [ScopedNoteHash::empty(); 10]; - let mut reversed_nullifiers = [ScopedNullifier::empty(); 10]; - - for i in 0..10 { - reversed_note_hashes[9 - i] = builder.previous_kernel.note_hashes.pop(); - reversed_nullifiers[9 - i] = builder.previous_kernel.nullifiers.pop(); - } - - builder.previous_kernel.note_hashes.extend_from_array(reversed_note_hashes); - builder.previous_kernel.nullifiers.extend_from_array(reversed_nullifiers); - - let public_inputs = builder.execute(); - - let expected_note_hashes = builder.compute_output_note_hashes(sorted_note_hashes); - let expected_nullifiers = builder.compute_output_nullifiers(sorted_nullifiers); - for i in 0..10 { - assert(public_inputs.end.note_hashes[i].eq(expected_note_hashes[i])); - assert(public_inputs.end.nullifiers[i].eq(expected_nullifiers[i])); - } - } - - #[test] - fn native_empty_nullified_note_hash_means_persistent_nullifier_0() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_note_hashes(2); - builder.previous_kernel.append_nullifiers(2); - builder.previous_kernel.add_note_encrypted_log_hash( - 42, - 5, - builder.previous_kernel.note_hashes.get(0).note_hash.counter - ); - let public_inputs = builder.execute(); - assert_eq(array_length(public_inputs.end.note_hashes), 2); - assert_eq(array_length(public_inputs.end.nullifiers), 3); - let expected_gas = Gas::tx_overhead() + Gas::new( - DA_GAS_PER_BYTE * DA_BYTES_PER_FIELD * 5 + DA_GAS_PER_BYTE * 5, - L2_GAS_PER_NOTE_HASH * 2 + L2_GAS_PER_NULLIFIER * 3 + L2_GAS_PER_LOG_BYTE * 5 - ); - assert_eq(public_inputs.end.gas_used, expected_gas); - } - #[test(should_fail_with="Private call stack must be empty when executing the tail circuit")] fn non_empty_private_call_stack_should_fail() { let mut builder = PrivateKernelTailInputsBuilder::new(); @@ -401,11 +258,11 @@ mod tests { #[test] unconstrained fn tx_consumed_gas_from_logs() { let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.add_encrypted_log_hash(42, 3); - builder.previous_kernel.add_encrypted_log_hash(42, 4); + builder.previous_kernel.add_masked_encrypted_log_hash(42, 3); + builder.previous_kernel.add_masked_encrypted_log_hash(42, 4); builder.previous_kernel.add_unencrypted_log_hash(42, 5); builder.previous_kernel.end_setup(); - builder.previous_kernel.add_encrypted_log_hash(42, 6); + builder.previous_kernel.add_masked_encrypted_log_hash(42, 6); builder.previous_kernel.add_unencrypted_log_hash(42, 7); let public_inputs = builder.execute(); 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 6c16401e905c..ec83a1f14152 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 @@ -36,8 +36,7 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { let output = self.generate_output(); // Validate inputs. - let previous_kernel_validator = PreviousKernelValidator::new(self.previous_kernel.public_inputs); - previous_kernel_validator.validate_for_private_tail_to_public(); + PreviousKernelValidator::new(self.previous_kernel.public_inputs).validate_for_private_tail_to_public(); if !std::runtime::is_unconstrained() { // TODO(#7410) currently stubbed out until tube vk handled // self.previous_kernel.validate_in_vk_tree(ALLOWED_PREVIOUS_CIRCUITS); @@ -45,11 +44,7 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { // Validate output. if dep::types::validate::should_validate_output() { - TailToPublicOutputValidator::new( - output, - self.previous_kernel.public_inputs, - previous_kernel_validator.hints - ).validate(); + TailToPublicOutputValidator::new(output, self.previous_kernel.public_inputs).validate(); } output @@ -68,8 +63,8 @@ mod tests { note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, log_hash::{LogHash, NoteLogHash} }, - address::{AztecAddress, EthAddress}, hash::{silo_note_hash, silo_nullifier}, - tests::fixture_builder::FixtureBuilder, utils::{arrays::array_eq}, point::Point, + address::{AztecAddress, EthAddress}, tests::fixture_builder::FixtureBuilder, + utils::{arrays::array_eq}, point::Point, constants::{BASE_ROLLUP_INDEX, PRIVATE_KERNEL_INNER_INDEX} }; @@ -91,29 +86,23 @@ mod tests { // 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_output_note_hashes(self, note_hashes: [ScopedNoteHash; N]) -> [ScopedNoteHash; N] { - // First nullifier is tx hash. - let tx_hash = self.previous_kernel.nullifiers.get_unchecked(0).value(); + pub fn compute_output_note_hashes(_self: Self, note_hashes: [ScopedNoteHash; N]) -> [ScopedNoteHash; N] { let mut output = [ScopedNoteHash::empty(); N]; for i in 0..N { - output[i] = NoteHash { - value: silo_note_hash(note_hashes[i], tx_hash, i), - counter: 0, // Counter is cleared so it's not exposed to the public. - }.scope(AztecAddress::zero()); + output[i].note_hash.value = note_hashes[i].value(); } output } - pub fn compute_output_nullifiers(_self: Self, nullifiers: [ScopedNullifier; N]) -> [Nullifier; N] { + pub fn compute_output_nullifiers(_self: Self, nullifiers: [ScopedNullifier; N]) -> [Nullifier; N] { let mut output = [Nullifier::empty(); N]; - output[0].value = nullifiers[0].value(); - for i in 1..N { - output[i] = Nullifier { value: silo_nullifier(nullifiers[i]), counter: 0, note_hash: 0 }; + for i in 0..N { + output[i].value = nullifiers[i].value(); } output } - pub fn compute_output_note_logs(_self: Self, logs: [NoteLogHash; N]) -> [LogHash; N] { + pub fn compute_output_note_logs(_self: Self, logs: [NoteLogHash; N]) -> [LogHash; N] { let mut output = [LogHash::empty(); N]; for i in 0..N { if logs[i].value != 0 { @@ -137,39 +126,6 @@ mod tests { } } - #[test] - fn ordering_of_note_hashes_and_nullifiers() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_note_hashes(10); - builder.previous_kernel.append_nullifiers(10); - - let sorted_note_hashes = builder.previous_kernel.note_hashes.storage; - let sorted_nullifiers = builder.previous_kernel.nullifiers.storage; - - let mut reversed_note_hashes = [ScopedNoteHash::empty(); 10]; - let mut reversed_nullifiers = [ScopedNullifier::empty(); 10]; - - for i in 0..10 { - reversed_note_hashes[9 - i] = builder.previous_kernel.note_hashes.pop(); - reversed_nullifiers[9 - i] = builder.previous_kernel.nullifiers.pop(); - } - - builder.previous_kernel.note_hashes.extend_from_array(reversed_note_hashes); - builder.previous_kernel.nullifiers.extend_from_array(reversed_nullifiers); - - let public_inputs = builder.execute(); - - let first_nullifier = builder.previous_kernel.nullifiers.get(0); - assert_eq(public_inputs.end_non_revertible.nullifiers[0], first_nullifier.nullifier); - let output_note_hashes = builder.compute_output_note_hashes(sorted_note_hashes); - let output_nullifiers = builder.compute_output_nullifiers(sorted_nullifiers); - for i in 0..10 { - assert(public_inputs.end.note_hashes[i].eq(output_note_hashes[i])); - assert(public_inputs.end.nullifiers[i].eq(output_nullifiers[i + 1])); - } - } - #[test(should_fail_with="Private call stack must be empty when executing the tail circuit")] fn non_empty_private_call_stack_should_fail() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); @@ -214,11 +170,11 @@ mod tests { fn split_nullifiers_into_non_revertible() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); // expect 3 non-revertible nullifiers: the tx nullifier + 2 new ones - builder.previous_kernel.append_nullifiers(2); + builder.previous_kernel.append_siloed_nullifiers(2); builder.previous_kernel.end_setup(); // expect 2 revertible nullifiers - builder.previous_kernel.append_nullifiers(2); + builder.previous_kernel.append_siloed_nullifiers(2); let nullifiers = builder.previous_kernel.nullifiers.storage; let public_inputs = builder.execute(); @@ -259,63 +215,41 @@ mod tests { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); // expect 2 non-revertible note hashes - builder.previous_kernel.append_note_hashes_with_logs(2); + builder.previous_kernel.append_siloed_note_hashes(2); builder.previous_kernel.end_setup(); // expect 2 revertible note hashes - builder.previous_kernel.append_note_hashes_with_logs(2); + builder.previous_kernel.append_siloed_note_hashes(2); let note_hashes = builder.previous_kernel.note_hashes.storage; - let note_logs = builder.previous_kernel.note_encrypted_logs_hashes.storage; let public_inputs = builder.execute(); - let siloed_note_hashes = builder.compute_output_note_hashes(note_hashes); - let public_note_logs = builder.compute_output_note_logs(note_logs); + let exposed_note_hashes = builder.compute_output_note_hashes(note_hashes); assert( array_eq( public_inputs.end_non_revertible.note_hashes, - [siloed_note_hashes[0], siloed_note_hashes[1]] - ) - ); - - assert( - array_eq( - public_inputs.end_non_revertible.note_encrypted_logs_hashes, - [public_note_logs[0], public_note_logs[1]] + [exposed_note_hashes[0], exposed_note_hashes[1]] ) ); assert( array_eq( public_inputs.end.note_hashes, - [siloed_note_hashes[2], siloed_note_hashes[3]] + [exposed_note_hashes[2], exposed_note_hashes[3]] ) ); - assert( - array_eq( - public_inputs.end.note_encrypted_logs_hashes, - [public_note_logs[2], public_note_logs[3]] - ) - ); - - let revertible_logs_len = (note_logs[2].length + note_logs[3].length) as u32; - let non_revertible_logs_len = (note_logs[0].length + note_logs[1].length) as u32; - assert_eq( public_inputs.end.gas_used, Gas::new( - (2 * DA_BYTES_PER_FIELD + revertible_logs_len) * DA_GAS_PER_BYTE, - 2 * L2_GAS_PER_NOTE_HASH + revertible_logs_len * L2_GAS_PER_LOG_BYTE + (2 * DA_BYTES_PER_FIELD) * DA_GAS_PER_BYTE, + 2 * L2_GAS_PER_NOTE_HASH ) ); assert_eq( public_inputs.end_non_revertible.gas_used, Gas::new( - (3 * DA_BYTES_PER_FIELD + non_revertible_logs_len) * DA_GAS_PER_BYTE, - FIXED_AVM_STARTUP_L2_GAS - + L2_GAS_PER_NULLIFIER - + 2 * L2_GAS_PER_NOTE_HASH - + non_revertible_logs_len * L2_GAS_PER_LOG_BYTE + (3 * DA_BYTES_PER_FIELD) * DA_GAS_PER_BYTE, + FIXED_AVM_STARTUP_L2_GAS + L2_GAS_PER_NULLIFIER + 2 * L2_GAS_PER_NOTE_HASH ) + Gas::tx_overhead() ); @@ -410,11 +344,11 @@ mod tests { #[test] unconstrained fn tx_consumed_gas_from_logs() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - builder.previous_kernel.add_encrypted_log_hash(42, 3); - builder.previous_kernel.add_encrypted_log_hash(42, 4); + builder.previous_kernel.add_masked_encrypted_log_hash(42, 3); + builder.previous_kernel.add_masked_encrypted_log_hash(42, 4); builder.previous_kernel.add_unencrypted_log_hash(42, 5); builder.previous_kernel.end_setup(); - builder.previous_kernel.add_encrypted_log_hash(42, 6); + builder.previous_kernel.add_masked_encrypted_log_hash(42, 6); builder.previous_kernel.add_unencrypted_log_hash(42, 7); let public_inputs = builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder/validate_no_transient_data.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder/validate_no_transient_data.nr index 4fa80ad85b78..ac9a535e01d7 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder/validate_no_transient_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder/validate_no_transient_data.nr @@ -1,15 +1,12 @@ use crate::tests::previous_kernel_validator_builder::PreviousKernelValidatorBuilder; -use dep::types::hash::silo_note_hash; impl PreviousKernelValidatorBuilder { pub fn append_same_inner_note_hashes(&mut self) -> [Field; 4] { let mut siloed_note_hashes = [0; 4]; let inner_note_hash = 123123; - let tx_hash = self.previous_kernel.nullifiers.storage[0].value(); for i in 0..siloed_note_hashes.len() { - self.previous_kernel.add_new_note_hash(inner_note_hash); - let note = self.previous_kernel.note_hashes.storage[i]; - siloed_note_hashes[i] = silo_note_hash(note, tx_hash, i); + self.previous_kernel.add_siloed_note_hash(inner_note_hash); + siloed_note_hashes[i] = self.previous_kernel.note_hashes.storage[i].note_hash.value; } siloed_note_hashes } @@ -25,7 +22,7 @@ fn validate_no_transient_data_no_extra_nullifiers_succeeds() { fn validate_no_transient_data_no_transient_nullifiers_succeeds() { let mut builder = PreviousKernelValidatorBuilder::new(); - builder.previous_kernel.append_nullifiers(3); + builder.previous_kernel.append_siloed_nullifiers(3); builder.validate_for_private_tail(); } @@ -35,10 +32,10 @@ fn validate_no_transient_data_nullifiers_for_note_hashes_succeeds() { let mut builder = PreviousKernelValidatorBuilder::new(); let siloed_note_hashes = builder.append_same_inner_note_hashes(); - builder.previous_kernel.add_nullifier(1); - builder.previous_kernel.add_nullifier_for_note_hash(2, siloed_note_hashes[2]); - builder.previous_kernel.add_nullifier_for_note_hash(3, siloed_note_hashes[0]); - builder.previous_kernel.add_nullifier(4); + builder.previous_kernel.add_siloed_nullifier(1); + builder.previous_kernel.add_siloed_nullifier_for_note_hash(2, siloed_note_hashes[2]); + builder.previous_kernel.add_siloed_nullifier_for_note_hash(3, siloed_note_hashes[0]); + builder.previous_kernel.add_siloed_nullifier(4); builder.validate_for_private_tail(); } @@ -47,7 +44,7 @@ fn validate_no_transient_data_nullifiers_for_note_hashes_succeeds() { fn validate_no_transient_data_nullifiers_for_note_hashes_emitted_after_fails() { let mut builder = PreviousKernelValidatorBuilder::new(); - builder.previous_kernel.append_nullifiers(2); + builder.previous_kernel.append_siloed_nullifiers(2); // Emit the note hashes after the nullifiers let siloed_note_hashes = builder.append_same_inner_note_hashes(); // Link a note hash to the nullifier at index 2. @@ -61,11 +58,11 @@ fn validate_no_transient_data_nullifiers_for_note_hashes_not_found_fails() { let mut builder = PreviousKernelValidatorBuilder::new(); let siloed_note_hashes = builder.append_same_inner_note_hashes(); - builder.previous_kernel.add_nullifier(1); - builder.previous_kernel.add_nullifier_for_note_hash(2, siloed_note_hashes[2]); + builder.previous_kernel.add_siloed_nullifier(1); + builder.previous_kernel.add_siloed_nullifier_for_note_hash(2, siloed_note_hashes[2]); // Assign a random note hash for nullifier 3. - builder.previous_kernel.add_nullifier_for_note_hash(3, 123); - builder.previous_kernel.add_nullifier(4); + builder.previous_kernel.add_siloed_nullifier_for_note_hash(3, 123); + builder.previous_kernel.add_siloed_nullifier(4); builder.validate_for_private_tail(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder/mod.nr index 0bdc19122bd9..2e230c87155f 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder/mod.nr @@ -14,12 +14,6 @@ impl TailOutputComposerBuilder { TailOutputComposerBuilder { previous_kernel } } - pub fn with_siloed_data_builder(self) -> (Self, FixtureBuilder) { - let mut siloed_data_builder = FixtureBuilder::new(); - siloed_data_builder.set_first_nullifier(); - (self, siloed_data_builder) - } - pub fn finish(self) -> KernelCircuitPublicInputs { let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); let composer = TailOutputComposer::new(previous_kernel); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/mod.nr index cbb0fc236f5a..db8df7506b51 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/mod.nr @@ -1,11 +1,9 @@ -mod validate_accumulated_values; mod validate_empty_values; mod validate_gas_used; -mod validate_propagated_sorted_siloed_values; +mod validate_propagated_sorted_values; mod validate_propagated_values; use crate::components::{ - previous_kernel_validator::previous_kernel_validator_hints::{generate_previous_kernel_validator_hints, PreviousKernelValidatorHints}, tail_output_composer::meter_gas_used::meter_gas_used, tail_output_validator::{tail_output_hints::{generate_tail_output_hints, TailOutputHints}, TailOutputValidator} }; @@ -38,11 +36,6 @@ impl TailOutputValidatorBuilder { generate_tail_output_hints(previous_kernel) } - pub fn get_previous_kernel_hints(self) -> PreviousKernelValidatorHints { - let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - generate_previous_kernel_validator_hints(previous_kernel) - } - pub fn export_output(self) -> KernelCircuitPublicInputs { let mut output = self.output.to_kernel_circuit_public_inputs(); output.end.gas_used = meter_gas_used(output.end, output.constants.tx_context.gas_settings); @@ -56,20 +49,12 @@ impl TailOutputValidatorBuilder { pub fn validate_with_output(self, output: KernelCircuitPublicInputs) { let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - let previous_kernel_hints = generate_previous_kernel_validator_hints(previous_kernel); - TailOutputValidator::new(output, previous_kernel, previous_kernel_hints).validate(); + TailOutputValidator::new(output, previous_kernel).validate(); } pub fn validate_with_hints(self, hints: TailOutputHints) { let output = self.export_output(); let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - let previous_kernel_hints = generate_previous_kernel_validator_hints(previous_kernel); - TailOutputValidator { output, previous_kernel, previous_kernel_hints, hints }.validate(); - } - - pub fn validate_with_previous_kernel_hints(self, previous_kernel_hints: PreviousKernelValidatorHints) { - let output = self.export_output(); - let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - TailOutputValidator::new(output, previous_kernel, previous_kernel_hints).validate(); + TailOutputValidator { output, previous_kernel, hints }.validate(); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_accumulated_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_accumulated_values.nr deleted file mode 100644 index e38c86f8ef90..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_accumulated_values.nr +++ /dev/null @@ -1,123 +0,0 @@ -use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; -use dep::types::tests::utils::swap_items; - -// TODO: Add tests that fail to validate with tweaked hints. - -/** - * note_encrypted_log_hashes - */ - -#[test] -fn validate_accumulated_values_note_encrypted_log_hashes_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_encrypted_log_hashes(3); - builder.output.append_note_encrypted_log_hashes(3); - - builder.validate(); -} - -#[test] -fn validate_accumulated_values_note_encrypted_log_hashes_unordered_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_encrypted_log_hashes(3); - // Swap the items at index 0 and 2. - swap_items(&mut builder.previous_kernel.note_encrypted_logs_hashes, 0, 2); - builder.output.append_note_encrypted_log_hashes(3); - - builder.validate(); -} - -#[test(should_fail_with="mismatch note_encrypted_logs_hashes")] -fn validate_accumulated_values_note_encrypted_log_hashes_wrong_hash_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_encrypted_log_hashes(3); - builder.output.append_note_encrypted_log_hashes(3); - // Swap the items in the output, making them out of order, and then hash. - swap_items(&mut builder.output.note_encrypted_logs_hashes, 0, 2); - - builder.validate(); -} - -/** - * encrypted_log_hashes - */ - -#[test] -fn validate_accumulated_values_encrypted_log_hashes_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_encrypted_log_hashes(3); - builder.output.append_encrypted_log_hashes(3); - builder.output.mask_encrypted_log_hashes(); - - builder.validate(); -} - -#[test] -fn validate_accumulated_values_encrypted_log_hashes_unordered_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_encrypted_log_hashes(3); - // Swap the items at index 0 and 2. - swap_items(&mut builder.previous_kernel.encrypted_logs_hashes, 0, 2); - builder.output.append_encrypted_log_hashes(3); - builder.output.mask_encrypted_log_hashes(); - - builder.validate(); -} - -#[test(should_fail_with="mismatch sorted values")] -fn validate_accumulated_values_encrypted_log_hashes_wrong_hash_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_encrypted_log_hashes(3); - builder.output.append_encrypted_log_hashes(3); - // Swap the items in the output, making them out of order, and then hash. - swap_items(&mut builder.output.encrypted_logs_hashes, 0, 2); - builder.output.mask_encrypted_log_hashes(); - - builder.validate(); -} - -/** - * unencrypted_log_hashes - */ - -#[test] -fn validate_accumulated_values_unencrypted_log_hashes_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_unencrypted_log_hashes(3); - builder.output.append_unencrypted_log_hashes(3); - builder.output.hash_unencrypted_log_hashes(); - - builder.validate(); -} - -#[test] -fn validate_accumulated_values_unencrypted_log_hashes_unordered_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_unencrypted_log_hashes(3); - // Swap the items at index 0 and 2. - swap_items(&mut builder.previous_kernel.unencrypted_logs_hashes, 0, 2); - builder.output.append_unencrypted_log_hashes(3); - builder.output.hash_unencrypted_log_hashes(); - - builder.validate(); -} - -#[test(should_fail_with="mismatch unencrypted_logs_hashes")] -fn validate_accumulated_values_unencrypted_log_hashes_wrong_order_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_unencrypted_log_hashes(3); - builder.output.append_unencrypted_log_hashes(3); - // Swap the items in the output, making them out of order - swap_items(&mut builder.output.unencrypted_logs_hashes, 0, 2); - - builder.validate(); -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_gas_used.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_gas_used.nr index 7e5bb1391322..070998014157 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_gas_used.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_gas_used.nr @@ -4,7 +4,7 @@ impl TailOutputValidatorBuilder { pub fn new_with_data() -> Self { let mut builder = TailOutputValidatorBuilder::new(); - builder.previous_kernel.append_note_hashes(3); + builder.previous_kernel.append_siloed_note_hashes(3); builder.output.append_siloed_note_hashes(3); builder.previous_kernel.append_note_encrypted_log_hashes(3); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_siloed_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_siloed_values.nr deleted file mode 100644 index 7004fcf4ed52..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_siloed_values.nr +++ /dev/null @@ -1,130 +0,0 @@ -use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; -use dep::types::tests::utils::swap_items; - -/** - * note_hashes - */ - -#[test] -fn validate_propagated_sorted_siloed_values_note_hashes_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_hashes(3); - builder.output.append_siloed_note_hashes(3); - - builder.validate(); -} - -#[test] -fn validate_propagated_sorted_siloed_values_note_hashes_unordered_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_hashes(3); - for i in 0..3 { - // Need to silo the note hashes in the right order to hash with the correct index. - builder.output.add_siloed_note_hash(builder.previous_kernel.note_hashes.storage[i].value()); - } - swap_items(&mut builder.previous_kernel.note_hashes, 0, 2); - - builder.validate(); -} - -#[test(should_fail_with="mismatch sorted values")] -fn validate_propagated_sorted_siloed_values_note_hashes_mismatch_sorted_hash_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_hashes(2); - builder.output.append_siloed_note_hashes(2); - // Tweak the hash in the output. - builder.output.note_hashes.storage[0].note_hash.value += 1; - - builder.validate(); -} - -#[test(should_fail_with="mismatch siloed note hashes")] -fn validate_propagated_sorted_siloed_values_note_hashes_mismatch_siloed_hash_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_note_hashes(2); - builder.output.append_siloed_note_hashes(2); - - let mut hints = builder.get_previous_kernel_hints(); - // Tweak the hash in the hints. - hints.siloed_note_hashes[0] += 1; - - builder.validate_with_previous_kernel_hints(hints); -} - -/** - * nullifiers - */ - -#[test] -fn validate_propagated_sorted_siloed_values_nullifiers_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_nullifiers(3); - builder.output.append_siloed_nullifiers(3); - - builder.validate(); -} - -#[test] -fn validate_propagated_sorted_siloed_values_nullifiers_unordered_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_nullifiers(3); - swap_items(&mut builder.previous_kernel.nullifiers, 0, 3); - builder.output.append_siloed_nullifiers(3); - - builder.validate(); -} - -#[test(should_fail_with="mismatch sorted values")] -fn validate_propagated_sorted_siloed_values_nullifiers_mismatch_hash_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_nullifiers(3); - builder.output.append_siloed_nullifiers(3); - // Tweak the hash in the output. - builder.output.nullifiers.storage[0].nullifier.value += 1; - - builder.validate(); -} - -/** - * l2_to_l1_msgs - */ - -#[test] -fn validate_propagated_sorted_siloed_values_l2_to_l1_msgs_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_l2_to_l1_msgs(2); - builder.output.append_exposed_l2_to_l1_msgs(2); - - builder.validate(); -} - -#[test] -fn validate_propagated_sorted_siloed_values_l2_to_l1_msgs_unordered_succeeds() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_l2_to_l1_msgs(2); - swap_items(&mut builder.previous_kernel.l2_to_l1_msgs, 0, 1); - builder.output.append_exposed_l2_to_l1_msgs(2); - - builder.validate(); -} - -#[test(should_fail_with="mismatch sorted values")] -fn validate_propagated_sorted_siloed_values_l2_to_l1_msgs_mismatch_hash_fails() { - let mut builder = TailOutputValidatorBuilder::new(); - - builder.previous_kernel.append_l2_to_l1_msgs(2); - builder.output.append_exposed_l2_to_l1_msgs(2); - // Tweak the content in the output. - builder.output.l2_to_l1_msgs.storage[0].message.content += 1; - - builder.validate(); -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr new file mode 100644 index 000000000000..8b539fb2dfda --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr @@ -0,0 +1,79 @@ +use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; +use dep::types::tests::utils::swap_items; + +/** + * l2_to_l1_msgs + */ + +#[test] +fn validate_propagated_sorted_values_l2_to_l1_msgs_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_l2_to_l1_msgs(2); + builder.output.append_exposed_l2_to_l1_msgs(2); + + builder.validate(); +} + +#[test] +fn validate_propagated_sorted_values_l2_to_l1_msgs_unordered_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_l2_to_l1_msgs(2); + swap_items(&mut builder.previous_kernel.l2_to_l1_msgs, 0, 1); + builder.output.append_exposed_l2_to_l1_msgs(2); + + builder.validate(); +} + +#[test(should_fail_with="mismatch sorted values")] +fn validate_propagated_sorted_values_l2_to_l1_msgs_mismatch_hash_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_l2_to_l1_msgs(2); + builder.output.append_exposed_l2_to_l1_msgs(2); + // Tweak the content in the output. + builder.output.l2_to_l1_msgs.storage[0].message.content += 1; + + builder.validate(); +} + +/** + * unencrypted_log_hashes + */ + +#[test] +fn validate_propagated_sorted_values_unencrypted_log_hashes_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_unencrypted_log_hashes(3); + builder.output.append_unencrypted_log_hashes(3); + builder.output.hash_unencrypted_log_hashes(); + + builder.validate(); +} + +#[test] +fn validate_propagated_sorted_values_unencrypted_log_hashes_unordered_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_unencrypted_log_hashes(3); + // Swap the items at index 0 and 2. + swap_items(&mut builder.previous_kernel.unencrypted_logs_hashes, 0, 2); + builder.output.append_unencrypted_log_hashes(3); + builder.output.hash_unencrypted_log_hashes(); + + builder.validate(); +} + +#[test(should_fail_with="mismatch unencrypted_logs_hashes")] +fn validate_propagated_sorted_values_unencrypted_log_hashes_wrong_order_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_unencrypted_log_hashes(3); + builder.output.append_unencrypted_log_hashes(3); + // Swap the items in the output, making them out of order + swap_items(&mut builder.output.unencrypted_logs_hashes, 0, 2); + + builder.validate(); +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_values.nr index f5bea6db72fe..a45b16018bbb 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_values.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_values.nr @@ -1,6 +1,10 @@ use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; use dep::types::address::AztecAddress; +/** + * constants + */ + #[test] fn validate_propagated_values_constants_succeeds() { let mut builder = TailOutputValidatorBuilder::new(); @@ -22,6 +26,10 @@ fn validate_propagated_values_constants_mismatch_fails() { builder.validate(); } +/** + * max_block_number + */ + #[test] fn validate_propagated_values_max_block_number_succeeds() { let mut builder = TailOutputValidatorBuilder::new(); @@ -43,6 +51,10 @@ fn validate_propagated_values_max_block_number_mismatch_fails() { builder.validate(); } +/** + * fee_payer + */ + #[test] fn validate_propagated_values_fee_payer_succeeds() { let mut builder = TailOutputValidatorBuilder::new(); @@ -63,3 +75,161 @@ fn validate_propagated_values_fee_payer_mismatch_fails() { builder.validate(); } + +/** + * note_hashes + */ + +#[test] +fn validate_propagated_values_note_hashes_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_note_hashes(3); + builder.output.append_note_hashes(3); + + builder.validate(); +} + +#[test(should_fail_with="mismatch note_hashes")] +fn validate_propagated_values_note_hashes_mismatch_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_note_hashes(3); + builder.output.append_note_hashes(3); + + // Tweak the value at index 1. + builder.output.note_hashes.storage[1].note_hash.value += 1; + + builder.validate(); +} + +#[test(should_fail_with="mismatch note_hashes")] +fn validate_propagated_values_note_hashes_extra_item_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_note_hashes(3); + // Append 1 more item. + builder.output.append_note_hashes(4); + + builder.validate(); +} + +/** + * nullifiers + */ + +#[test] +fn validate_propagated_values_nullifiers_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_nullifiers(3); + builder.output.append_nullifiers(3); + + builder.validate(); +} + +#[test(should_fail_with="mismatch nullifiers")] +fn validate_propagated_values_nullifiers_mismatch_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_nullifiers(3); + builder.output.append_nullifiers(3); + + // Tweak the value at index 1. + builder.output.nullifiers.storage[1].nullifier.value += 1; + + builder.validate(); +} + +#[test(should_fail_with="mismatch nullifiers")] +fn validate_propagated_values_nullifiers_extra_item_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_nullifiers(3); + // Append 1 more item. + builder.output.append_nullifiers(4); + + builder.validate(); +} + +/** + * note_encrypted_log_hashes + */ + +#[test] +fn validate_propagated_values_note_encrypted_log_hashes_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_note_encrypted_log_hashes(3); + builder.output.append_note_encrypted_log_hashes(3); + + builder.validate(); +} + +#[test(should_fail_with="mismatch note_encrypted_logs_hashes")] +fn validate_propagated_values_note_encrypted_log_hashes_mismatch_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_note_encrypted_log_hashes(3); + builder.output.append_note_encrypted_log_hashes(3); + + // Tweak the value at index 1. + builder.output.note_encrypted_logs_hashes.storage[1].value += 1; + + builder.validate(); +} + +#[test(should_fail_with="mismatch note_encrypted_logs_hashes")] +fn validate_propagated_values_note_encrypted_log_hashes_non_zero_counter_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_note_encrypted_log_hashes(3); + builder.output.append_note_encrypted_log_hashes(3); + + let mut output = builder.export_output(); + // Set the counter at index 1. + output.end.note_encrypted_logs_hashes[1].counter = builder.previous_kernel.note_encrypted_logs_hashes.storage[1].counter; + + builder.validate_with_output(output); +} + +/** + * encrypted_log_hashes + */ + +#[test] +fn validate_propagated_values_encrypted_log_hashes_succeeds() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_encrypted_log_hashes(3); + builder.output.append_encrypted_log_hashes(3); + + builder.validate(); +} + +#[test(should_fail_with="mismatch encrypted_logs_hashes")] +fn validate_propagated_values_encrypted_logs_hashes_mismatch_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_encrypted_log_hashes(3); + builder.output.append_encrypted_log_hashes(3); + + // Tweak the value at index 1. + builder.output.encrypted_logs_hashes.storage[1].log_hash.value += 1; + + builder.validate(); +} + +#[test(should_fail_with="mismatch encrypted_logs_hashes")] +fn validate_propagated_values_encrypted_logs_hashes_non_zero_counter_fails() { + let mut builder = TailOutputValidatorBuilder::new(); + + builder.previous_kernel.append_encrypted_log_hashes(3); + builder.output.append_encrypted_log_hashes(3); + + let mut output = builder.export_output(); + // Set the counter at index 1. + output.end.encrypted_logs_hashes[1].log_hash.counter = builder.previous_kernel.encrypted_logs_hashes.storage[1].log_hash.counter; + + builder.validate_with_output(output); +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/mod.nr index 1d0042bf686d..38ecbc234c5e 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/mod.nr @@ -19,12 +19,6 @@ impl TailToPublicOutputComposerBuilder { TailToPublicOutputComposerBuilder { previous_kernel } } - pub fn with_siloed_data_builder(self) -> (Self, FixtureBuilder) { - let mut siloed_data_builder = FixtureBuilder::new(); - siloed_data_builder.set_first_nullifier(); - (self, siloed_data_builder) - } - pub fn finish(self) -> PublicKernelCircuitPublicInputs { let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); let composer = TailToPublicOutputComposer::new(previous_kernel); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr index 53c8d4734880..7cc20511d4aa 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr @@ -10,129 +10,121 @@ use dep::types::{ #[test] fn tail_to_public_output_composer_succeeds() { - let mut (builder, siloed_data_builder) = TailToPublicOutputComposerBuilder::new().with_siloed_data_builder(); + let mut builder = TailToPublicOutputComposerBuilder::new(); let teardown_gas = Gas::new(789, 3254); builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = teardown_gas; // Non-revertibles. - builder.previous_kernel.append_note_hashes(4); - siloed_data_builder.append_siloed_note_hashes(4); + builder.previous_kernel.append_siloed_note_hashes(4); - builder.previous_kernel.append_nullifiers(2); - siloed_data_builder.append_siloed_nullifiers(2); + builder.previous_kernel.append_siloed_nullifiers(2); builder.previous_kernel.append_l2_to_l1_msgs(1); - siloed_data_builder.append_exposed_l2_to_l1_msgs(1); builder.previous_kernel.add_note_encrypted_log_hash(1001, 12, 0); builder.previous_kernel.add_note_encrypted_log_hash(1002, 8, 0); - builder.previous_kernel.add_encrypted_log_hash(2001, 2); - siloed_data_builder.add_masked_encrypted_log_hash(2001, 2); + builder.previous_kernel.add_masked_encrypted_log_hash(2001, 2); builder.previous_kernel.add_unencrypted_log_hash(3001, 51); + builder.previous_kernel.add_unencrypted_log_hash(3002, 9); builder.previous_kernel.append_public_call_requests(2); builder.previous_kernel.end_setup(); // Revertibles. - builder.previous_kernel.append_note_hashes(2); - siloed_data_builder.append_siloed_note_hashes(2); + builder.previous_kernel.append_siloed_note_hashes(2); - builder.previous_kernel.append_nullifiers(1); - siloed_data_builder.append_siloed_nullifiers(1); + builder.previous_kernel.append_siloed_nullifiers(1); builder.previous_kernel.append_l2_to_l1_msgs(1); - siloed_data_builder.append_exposed_l2_to_l1_msgs(1); builder.previous_kernel.add_note_encrypted_log_hash(1003, 20, 0); - builder.previous_kernel.add_encrypted_log_hash(2002, 6); - siloed_data_builder.add_masked_encrypted_log_hash(2002, 6); - builder.previous_kernel.add_encrypted_log_hash(2003, 24); - siloed_data_builder.add_masked_encrypted_log_hash(2003, 24); + builder.previous_kernel.add_masked_encrypted_log_hash(2002, 6); + builder.previous_kernel.add_masked_encrypted_log_hash(2003, 24); builder.previous_kernel.add_unencrypted_log_hash(3002, 4); builder.previous_kernel.append_public_call_requests(3); // Get ordered items before shuffling for verifying with the output later. - let siloed_data = siloed_data_builder.to_exposed_public_accumulated_data(); - let unsiloed_data = builder.previous_kernel.to_exposed_public_accumulated_data(); + let data = builder.previous_kernel.to_exposed_public_accumulated_data(); // Shuffle ordered items. - swap_items(&mut builder.previous_kernel.note_hashes, 4, 0); - swap_items(&mut builder.previous_kernel.note_hashes, 3, 2); - swap_items(&mut builder.previous_kernel.nullifiers, 1, 3); swap_items(&mut builder.previous_kernel.l2_to_l1_msgs, 0, 1); - swap_items(&mut builder.previous_kernel.note_encrypted_logs_hashes, 1, 2); - swap_items(&mut builder.previous_kernel.encrypted_logs_hashes, 1, 2); + swap_items(&mut builder.previous_kernel.unencrypted_logs_hashes, 0, 2); swap_items(&mut builder.previous_kernel.public_call_requests, 1, 2); // Output. let output = builder.finish(); // note_hashes - let siloed = siloed_data.note_hashes; + let note_hashes = data.note_hashes; assert_array_eq( output.end_non_revertible.note_hashes, - [siloed[0], siloed[1], siloed[2], siloed[3]] + [note_hashes[0], note_hashes[1], note_hashes[2], note_hashes[3]] ); - assert_array_eq(output.end.note_hashes, [siloed[4], siloed[5]]); + assert_array_eq(output.end.note_hashes, [note_hashes[4], note_hashes[5]]); // nullifiers - let siloed = siloed_data.nullifiers; - let unsiloed = unsiloed_data.nullifiers; + let nullifiers = data.nullifiers; assert_array_eq( output.end_non_revertible.nullifiers, - [unsiloed[0], siloed[1], siloed[2]] + [nullifiers[0], nullifiers[1], nullifiers[2]] ); - assert_array_eq(output.end.nullifiers, [siloed[3]]); + assert_array_eq(output.end.nullifiers, [nullifiers[3]]); // l2_to_l1_msgs - let siloed = siloed_data.l2_to_l1_msgs; - assert_array_eq(output.end_non_revertible.l2_to_l1_msgs, [siloed[0]]); - assert_array_eq(output.end.l2_to_l1_msgs, [siloed[1]]); + let msgs = data.l2_to_l1_msgs; + assert_array_eq(output.end_non_revertible.l2_to_l1_msgs, [msgs[0]]); + assert_array_eq(output.end.l2_to_l1_msgs, [msgs[1]]); // note_encrypted_logs_hashes - let unsiloed = unsiloed_data.note_encrypted_logs_hashes; + let log_hashes = data.note_encrypted_logs_hashes; assert_array_eq( output.end_non_revertible.note_encrypted_logs_hashes, - [unsiloed[0], unsiloed[1]] + [log_hashes[0], log_hashes[1]] ); - assert_array_eq(output.end.note_encrypted_logs_hashes, [unsiloed[2]]); + assert_array_eq(output.end.note_encrypted_logs_hashes, [log_hashes[2]]); // encrypted_logs_hashes - let siloed = siloed_data.encrypted_logs_hashes; - assert_array_eq(output.end_non_revertible.encrypted_logs_hashes, [siloed[0]]); - assert_array_eq(output.end.encrypted_logs_hashes, [siloed[1], siloed[2]]); + let log_hashes = data.encrypted_logs_hashes; + assert_array_eq( + output.end_non_revertible.encrypted_logs_hashes, + [log_hashes[0]] + ); + assert_array_eq( + output.end.encrypted_logs_hashes, + [log_hashes[1], log_hashes[2]] + ); // unencrypted_logs_hashes - let unsiloed = unsiloed_data.unencrypted_logs_hashes; + let log_hashes = data.unencrypted_logs_hashes; assert_array_eq( output.end_non_revertible.unencrypted_logs_hashes, - [unsiloed[0]] + [log_hashes[0], log_hashes[1]] ); - assert_array_eq(output.end.unencrypted_logs_hashes, [unsiloed[1]]); + assert_array_eq(output.end.unencrypted_logs_hashes, [log_hashes[2]]); // public_call_stack - let unsiloed = unsiloed_data.public_call_stack; + let call_requests = data.public_call_stack; assert_array_eq( output.end_non_revertible.public_call_stack, - [unsiloed[1], unsiloed[0]] + [call_requests[1], call_requests[0]] ); assert_array_eq( output.end.public_call_stack, - [unsiloed[4], unsiloed[3], unsiloed[2]] + [call_requests[4], call_requests[3], call_requests[2]] ); // Gas: non-revertible let total_num_side_effects = 4 + 3 + 1; let total_log_length = 12 + 8 // note_encrypted_log_hash + 2 // encrypted_log_hash - + 51; // unencrypted_log_hash + + 51 + 9; // unencrypted_log_hash let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; let computed_l2_gas = 4 * L2_GAS_PER_NOTE_HASH + 3 * L2_GAS_PER_NULLIFIER diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_validator_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_validator_builder.nr index d156f1175c77..27ddb515eb7d 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_validator_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_validator_builder.nr @@ -1,7 +1,4 @@ -use crate::components::{ - previous_kernel_validator::previous_kernel_validator_hints::generate_previous_kernel_validator_hints, - tail_to_public_output_validator::TailToPublicOutputValidator -}; +use crate::components::{tail_to_public_output_validator::TailToPublicOutputValidator}; use dep::types::tests::fixture_builder::FixtureBuilder; struct TailToPublicOutputValidatorBuilder { @@ -23,8 +20,7 @@ impl TailToPublicOutputValidatorBuilder { let revertible = true; let output = self.output.to_public_kernel_circuit_public_inputs(revertible); let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - let previous_kernel_hints = generate_previous_kernel_validator_hints(previous_kernel); - TailToPublicOutputValidator::new(output, previous_kernel, previous_kernel_hints).validate(); + TailToPublicOutputValidator::new(output, previous_kernel).validate(); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr index b9d08af2ccce..fbe34f8558b2 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr @@ -2,8 +2,8 @@ use dep::private_kernel_lib::PrivateKernelResetCircuitPrivateInputs; use dep::types::{ PrivateKernelCircuitPublicInputs, constants::{ - MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_KEY_VALIDATION_REQUESTS_PER_TX + MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NOTE_HASHES_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX } }; @@ -12,7 +12,14 @@ global NOTE_HASH_SETTLED_AMOUNT = MAX_NOTE_HASH_READ_REQUESTS_PER_TX; global NULLIFIER_PENDING_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; // 128 global NULLIFIER_SETTLED_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; global NULLIFIER_KEYS = MAX_KEY_VALIDATION_REQUESTS_PER_TX; // 64 +global NOTE_HASH_SILOING_AMOUNT = MAX_NOTE_HASHES_PER_TX; // 64 +global NULLIFIER_SILOING_AMOUNT = MAX_NULLIFIERS_PER_TX; // 64 +global ENCRYPTED_LOG_SILOING_AMOUNT = MAX_ENCRYPTED_LOGS_PER_TX; // 8 unconstrained fn main(input: PrivateKernelResetCircuitPrivateInputs) -> pub PrivateKernelCircuitPublicInputs { - input.execute() + input.execute( + NOTE_HASH_SILOING_AMOUNT, + NULLIFIER_SILOING_AMOUNT, + ENCRYPTED_LOG_SILOING_AMOUNT + ) } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr index 3b5e1a5d5e57..2eb141956a93 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr @@ -2,8 +2,8 @@ use dep::private_kernel_lib::PrivateKernelResetCircuitPrivateInputs; use dep::types::{ PrivateKernelCircuitPublicInputs, constants::{ - MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_KEY_VALIDATION_REQUESTS_PER_TX + MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NOTE_HASHES_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX } }; @@ -12,8 +12,15 @@ global NOTE_HASH_SETTLED_AMOUNT = MAX_NOTE_HASH_READ_REQUESTS_PER_TX; global NULLIFIER_PENDING_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; // 64 global NULLIFIER_SETTLED_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; global NULLIFIER_KEYS = MAX_KEY_VALIDATION_REQUESTS_PER_TX; // 64 +global NOTE_HASH_SILOING_AMOUNT = MAX_NOTE_HASHES_PER_TX; // 64 +global NULLIFIER_SILOING_AMOUNT = MAX_NULLIFIERS_PER_TX; // 64 +global ENCRYPTED_LOG_SILOING_AMOUNT = MAX_ENCRYPTED_LOGS_PER_TX; // 8 #[recursive] fn main(input: PrivateKernelResetCircuitPrivateInputs) -> pub PrivateKernelCircuitPublicInputs { - input.execute() + input.execute( + NOTE_HASH_SILOING_AMOUNT, + NULLIFIER_SILOING_AMOUNT, + ENCRYPTED_LOG_SILOING_AMOUNT + ) } 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 b7212a17419a..fbee3438a0ca 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 @@ -4,13 +4,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::{ - key_validation_hint::KeyValidationHint, - transient_data::{ - transient_data_reset_hints::get_transient_or_propagated_note_hash_indexes_for_logs, - verify_squashed_transient_data -} -}; +use reset::{key_validation_hint::KeyValidationHint, transient_data::verify_squashed_transient_data}; use dep::types::data::public_data_hint::PublicDataHint; mod note_hash_read_request_reset; 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 index 7e8f1eb084f8..ef8e2432e955 100644 --- 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 @@ -1,5 +1,3 @@ -mod transient_data_reset_hints; - use dep::types::{ abis::{note_hash::ScopedNoteHash, nullifier::ScopedNullifier, log_hash::NoteLogHash}, traits::is_empty 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 5f83702bbb50..c63525bf3f18 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -98,10 +98,11 @@ global NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP: u32 = 16; global PRIVATE_KERNEL_INIT_INDEX: u32 = 0; global PRIVATE_KERNEL_INNER_INDEX: u32 = 1; global PRIVATE_KERNEL_RESET_FULL_INDEX: u32 = 2; -global PRIVATE_KERNEL_RESET_BIG_INDEX: u32 = 3; -global PRIVATE_KERNEL_RESET_MEDIUM_INDEX: u32 = 4; -global PRIVATE_KERNEL_RESET_SMALL_INDEX: u32 = 5; -global PRIVATE_KERNEL_RESET_TINY_INDEX: u32 = 6; +global PRIVATE_KERNEL_RESET_FULL_INNER_INDEX: u32 = 3; +global PRIVATE_KERNEL_RESET_BIG_INDEX: u32 = 4; +global PRIVATE_KERNEL_RESET_MEDIUM_INDEX: u32 = 5; +global PRIVATE_KERNEL_RESET_SMALL_INDEX: u32 = 6; +global PRIVATE_KERNEL_RESET_TINY_INDEX: u32 = 7; global PRIVATE_KERNEL_TAIL_INDEX: u32 = 10; global PRIVATE_KERNEL_TAIL_TO_PUBLIC_INDEX: u32 = 11; global EMPTY_NESTED_INDEX: u32 = 12; 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 a03d4ddb24a8..b96c5dcd83e2 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 @@ -485,9 +485,9 @@ impl FixtureBuilder { // First nullifier is tx hash. let tx_hash = self.nullifiers.get(0).value(); let index = self.note_hashes.len(); - let note_hash = NoteHash { value, counter: 0 }.scope(self.storage_contract_address); - let siloed_value = silo_note_hash(note_hash, tx_hash, index); - self.add_new_note_hash(siloed_value); + let note_hash_to_silo = NoteHash { value, counter: 0 }.scope(self.storage_contract_address); + let siloed_value = silo_note_hash(note_hash_to_silo, tx_hash, index); + self.note_hashes.push(NoteHash { value: siloed_value, counter: self.next_counter() }.scope(AztecAddress::zero())); } pub fn append_note_hashes(&mut self, num_note_hashes: u32) { @@ -530,9 +530,8 @@ impl FixtureBuilder { } pub fn add_nullifier(&mut self, value: Field) { - self.nullifiers.push( - Nullifier { value, counter: self.next_counter(), note_hash: 0 }.scope(self.storage_contract_address) - ); + let note_hash = 0; + self.add_nullifier_for_note_hash(value, note_hash); } pub fn add_nullifier_for_note_hash(&mut self, value: Field, note_hash: Field) { @@ -542,8 +541,15 @@ impl FixtureBuilder { } pub fn add_siloed_nullifier(&mut self, value: Field) { + let note_hash = 0; + self.add_siloed_nullifier_for_note_hash(value, note_hash); + } + + pub fn add_siloed_nullifier_for_note_hash(&mut self, value: Field, note_hash: Field) { let siloed_value = compute_siloed_nullifier(self.storage_contract_address, value); - self.add_nullifier(siloed_value); + self.nullifiers.push( + Nullifier { value: siloed_value, counter: self.next_counter(), note_hash }.scope(AztecAddress::zero()) + ); } pub fn append_nullifiers(&mut self, num_extra_nullifier: u32) { @@ -728,6 +734,7 @@ impl FixtureBuilder { pub fn add_masked_encrypted_log_hash(&mut self, hash: Field, length: Field) { let mut log_hash = EncryptedLogHash { value: hash, counter: self.next_counter(), length, randomness: 2 }.scope(self.storage_contract_address); log_hash.contract_address = mask_encrypted_log_hash(log_hash); + log_hash.log_hash.randomness = 0; self.encrypted_logs_hashes.push(log_hash); self.encrypted_log_preimages_length += length; } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 9c2d8786cbe3..700a31066f57 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -1,6 +1,7 @@ mod assert_sorted_array; mod assert_sorted_transformed_value_array; mod assert_split_sorted_transformed_value_arrays; +mod assert_split_transformed_value_arrays; mod sort_by_counters; mod sort_get_order_hints; mod sort_get_sorted_hints; @@ -11,6 +12,7 @@ mod sort_get_split_order_hints; use assert_sorted_array::{assert_sorted_array_with_order_hints, assert_sorted_array}; use assert_split_sorted_transformed_value_arrays::{assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc}; use assert_sorted_transformed_value_array::assert_sorted_transformed_value_array; +use assert_split_transformed_value_arrays::assert_split_transformed_value_arrays; use sort_by_counters::{sort_by_counters_asc, sort_by_counters_desc}; use sort_get_order_hints::{OrderHint, sort_get_order_hints_asc, sort_get_order_hints_desc}; use sort_get_sorted_hints::sort_get_sorted_hints; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr index a1a2b00e9fbb..aa049308603f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr @@ -7,13 +7,13 @@ pub fn assert_sorted_transformed_value_array( transformed_value_array: [S; N], sorted_transformed_value_array: [S; N], hints: [OrderHint; N] -) where T: Ordered, S: Empty + Eq { +) where T: Ordered + Empty + Eq, S: Empty + Eq { for i in 0..N { let original = original_array[i]; - let value = transformed_value_array[i]; - if is_empty(value) { + if is_empty(original) { assert(is_empty(sorted_transformed_value_array[i]), "unexpected non-empty item"); } else { + let value = transformed_value_array[i]; let sorted_index = hints[i].sorted_index; assert_eq(value, sorted_transformed_value_array[sorted_index], "mismatch sorted values"); assert_eq(original.counter(), hints[sorted_index].counter, "mismatch counters"); @@ -54,6 +54,12 @@ mod tests { } } + impl Eq for TestItem { + fn eq(self, other: Self) -> bool { + (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) + } + } + struct TestValue { name: Field, total: Field, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr new file mode 100644 index 000000000000..5261582b5138 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_transformed_value_arrays.nr @@ -0,0 +1,264 @@ +use crate::{abis::side_effect::Ordered, traits::{Empty, is_empty}, utils::arrays::find_index}; + +pub fn assert_split_transformed_value_arrays_with_hint( + sorted_array: [T; N], + transformed_value_array_lt: [S; N], // Values whose counters are less than the split counter. + transformed_value_array_gte: [S; N], // Values whose counters are greater than or equal to the split counter. + is_transformed: fn[Env](T, S) -> bool, + split_counter: u32, + num_non_revertibles: u32 // Hint. Should check in this function. +) where T: Ordered + Empty + Eq, S: Empty + Eq { + if num_non_revertibles != 0 { + assert( + sorted_array[num_non_revertibles - 1].counter() < split_counter, "counter of last non-revertible item is not less than the split counter" + ); + } + + if num_non_revertibles != N { + let first_revertible_counter = sorted_array[num_non_revertibles].counter(); + assert( + (first_revertible_counter == 0) | (first_revertible_counter >= split_counter), "counter of first revertible item is not greater than or equal to the split counter" + ); + } + + let mut is_lt = true; + for i in 0..N { + is_lt &= i != num_non_revertibles; + + let from = sorted_array[i]; + let to = if is_lt { + transformed_value_array_lt[i] + } else { + transformed_value_array_gte[i - num_non_revertibles] + }; + assert(is_transformed(from, to), "invalid transformed value"); + + let padded = if is_lt { + transformed_value_array_gte[N - i - 1] + } else { + transformed_value_array_lt[i] + }; + assert(is_empty(padded), "array should be padded with empty items"); + } +} + +unconstrained fn get_num_non_revertibles( + sorted_array: [T; N], + split_counter: u32 +) -> u32 where T: Ordered { + find_index(sorted_array, |n: T| n.counter() >= split_counter) +} + +pub fn assert_split_transformed_value_arrays( + sorted_array: [T; N], + transformed_value_array_lt: [S; N], // Values whose counters are less than the split counter. + transformed_value_array_gte: [S; N], // Values whose counters are greater than or equal to the split counter. + is_transformed: fn[Env](T, S) -> bool, + split_counter: u32 +) where T: Ordered + Empty + Eq, S: Empty + Eq { + let num_non_revertibles = get_num_non_revertibles(sorted_array, split_counter); + + assert_split_transformed_value_arrays_with_hint( + sorted_array, + transformed_value_array_lt, + transformed_value_array_gte, + is_transformed, + split_counter, + num_non_revertibles + ); +} + +mod tests { + use crate::{ + abis::side_effect::Ordered, traits::Empty, + utils::arrays::assert_split_transformed_value_arrays::{assert_split_transformed_value_arrays, assert_split_transformed_value_arrays_with_hint} + }; + + struct TestItem { + value: Field, + counter: u32, + } + + impl Ordered for TestItem { + fn counter(self) -> u32 { + self.counter + } + } + + impl Eq for TestItem { + fn eq(self, other: Self) -> bool { + (self.value == other.value) & (self.counter == other.counter) + } + } + + impl Empty for TestItem { + fn empty() -> Self { + TestItem { value: 0, counter: 0 } + } + } + + global NUM_TEST_ITEMS = 5; + + fn is_transformed(from: TestItem, to: Field) -> bool { + from.value == to + } + + fn pad_end(items: [T; N], emptyItem: T) -> [T; NUM_TEST_ITEMS] { + let mut output = [emptyItem; NUM_TEST_ITEMS]; + for i in 0..N { + output[i] = items[i]; + } + output + } + + struct TestBuilder { + sorted_array: [TestItem; NUM_TEST_ITEMS], + transformed_value_array_lt: [Field; NUM_TEST_ITEMS], + transformed_value_array_gte: [Field; NUM_TEST_ITEMS], + split_counter: u32 + } + + impl TestBuilder { + pub fn new() -> Self { + let sorted_array = [ + TestItem { value: 40, counter: 2 }, + TestItem { value: 30, counter: 7 }, + TestItem { value: 80, counter: 11 }, + TestItem { value: 10, counter: 13 }, + TestItem { value: 50, counter: 29 } + ]; + + let transformed_value_array_lt = pad_end([40, 30, 80], 0); + + let transformed_value_array_gte = pad_end([10, 50], 0); + + TestBuilder { sorted_array, transformed_value_array_lt, transformed_value_array_gte, split_counter: 12 } + } + + pub fn execute(self) { + assert_split_transformed_value_arrays( + self.sorted_array, + self.transformed_value_array_lt, + self.transformed_value_array_gte, + is_transformed, + self.split_counter + ); + } + + pub fn execute_with_num_non_revertibles(self, num_non_revertibles: u32) { + assert_split_transformed_value_arrays_with_hint( + self.sorted_array, + self.transformed_value_array_lt, + self.transformed_value_array_gte, + is_transformed, + self.split_counter, + num_non_revertibles + ); + } + } + + #[test] + fn assert_split_transformed_value_arrays_succeeds() { + let builder = TestBuilder::new(); + builder.execute(); + } + + #[test] + fn assert_split_transformed_value_arrays_empty_succeeds() { + let mut builder = TestBuilder::new(); + + builder.sorted_array = [TestItem::empty(); NUM_TEST_ITEMS]; + builder.transformed_value_array_lt = [0; NUM_TEST_ITEMS]; + builder.transformed_value_array_gte = [0; NUM_TEST_ITEMS]; + + builder.execute(); + } + + #[test] + fn assert_split_transformed_value_arrays_empty_non_revertible_succeeds() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_lt = [0; NUM_TEST_ITEMS]; + builder.transformed_value_array_gte = [40, 30, 80, 10, 50]; + builder.split_counter = 1; + + builder.execute(); + } + + #[test] + fn assert_split_transformed_value_arrays_empty_revertible_succeeds() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_lt = [40, 30, 80, 10, 50]; + builder.transformed_value_array_gte = [0; NUM_TEST_ITEMS]; + builder.split_counter = 99; + + builder.execute(); + } + + #[test(should_fail_with="counter of first revertible item is not greater than or equal to the split counter")] + fn assert_split_transformed_value_arrays_num_non_revertible_too_small_fails() { + let builder = TestBuilder::new(); + builder.execute_with_num_non_revertibles(2); + } + + #[test(should_fail_with="counter of last non-revertible item is not less than the split counter")] + fn assert_split_transformed_value_arrays_num_non_revertible_too_big_fails() { + let mut builder = TestBuilder::new(); + builder.execute_with_num_non_revertibles(4); + } + + #[test(should_fail_with="invalid transformed value")] + fn assert_split_transformed_value_arrays_wrong_transformed_value_in_lt_fails() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_lt[1] += 1; + + builder.execute(); + } + + #[test(should_fail_with="invalid transformed value")] + fn assert_split_transformed_value_arrays_wrong_transformed_value_in_gte_fails() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_gte[1] += 1; + + builder.execute(); + } + + #[test(should_fail_with="array should be padded with empty items")] + fn assert_split_transformed_value_arrays_extra_non_empty_item_in_lt_fails() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_lt[3] = 1; + + builder.execute(); + } + + #[test(should_fail_with="array should be padded with empty items")] + fn assert_split_transformed_value_arrays_extra_non_empty_item_in_gte_fails() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_gte[2] = 1; + + builder.execute(); + } + + #[test(should_fail_with="array should be padded with empty items")] + fn assert_split_transformed_value_arrays_end_with_non_empty_item_in_lt_fails() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_lt[NUM_TEST_ITEMS - 1] = 1; + + builder.execute(); + } + + #[test(should_fail_with="array should be padded with empty items")] + fn assert_split_transformed_value_arrays_end_with_non_empty_item_in_gte_fails() { + let mut builder = TestBuilder::new(); + + builder.transformed_value_array_gte[NUM_TEST_ITEMS - 1] = 1; + + builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/generate_variants.js b/noir-projects/noir-protocol-circuits/generate_variants.js index a57793eae853..6c3b877cf38f 100644 --- a/noir-projects/noir-protocol-circuits/generate_variants.js +++ b/noir-projects/noir-protocol-circuits/generate_variants.js @@ -36,7 +36,10 @@ function generateVariants(originalFolder, variantsArray) { depDescriptor.path = "../" + depDescriptor.path; } } - variantNargoToml.package.name = `${originalName}_${tag}`; + variantNargoToml.package.name = `${originalName}_${tag.replaceAll( + "-", + "_" + )}`; let mainDotNoirCode = fs.readFileSync( path.join(originalCrateFolder, "src/main.nr"), diff --git a/noir-projects/noir-protocol-circuits/reset_variants.json b/noir-projects/noir-protocol-circuits/reset_variants.json index 88fdf280fa67..9c12eb4df34b 100644 --- a/noir-projects/noir-protocol-circuits/reset_variants.json +++ b/noir-projects/noir-protocol-circuits/reset_variants.json @@ -1,4 +1,18 @@ [ + { + "tag": "full-inner", + "priority": 97, + "replacements": { + "NOTE_HASH_PENDING_AMOUNT": 64, + "NOTE_HASH_SETTLED_AMOUNT": 64, + "NULLIFIER_PENDING_AMOUNT": 64, + "NULLIFIER_SETTLED_AMOUNT": 64, + "NULLIFIER_KEYS": 64, + "NOTE_HASH_SILOING_AMOUNT": 0, + "NULLIFIER_SILOING_AMOUNT": 0, + "ENCRYPTED_LOG_SILOING_AMOUNT": 0 + } + }, { "tag": "big", "priority": 98, @@ -7,7 +21,10 @@ "NOTE_HASH_SETTLED_AMOUNT": 32, "NULLIFIER_PENDING_AMOUNT": 32, "NULLIFIER_SETTLED_AMOUNT": 32, - "NULLIFIER_KEYS": 32 + "NULLIFIER_KEYS": 32, + "NOTE_HASH_SILOING_AMOUNT": 32, + "NULLIFIER_SILOING_AMOUNT": 32, + "ENCRYPTED_LOG_SILOING_AMOUNT": 8 } }, { @@ -18,7 +35,10 @@ "NOTE_HASH_SETTLED_AMOUNT": 16, "NULLIFIER_PENDING_AMOUNT": 16, "NULLIFIER_SETTLED_AMOUNT": 16, - "NULLIFIER_KEYS": 16 + "NULLIFIER_KEYS": 16, + "NOTE_HASH_SILOING_AMOUNT": 16, + "NULLIFIER_SILOING_AMOUNT": 16, + "ENCRYPTED_LOG_SILOING_AMOUNT": 4 } }, { @@ -29,7 +49,10 @@ "NOTE_HASH_SETTLED_AMOUNT": 8, "NULLIFIER_PENDING_AMOUNT": 8, "NULLIFIER_SETTLED_AMOUNT": 8, - "NULLIFIER_KEYS": 8 + "NULLIFIER_KEYS": 8, + "NOTE_HASH_SILOING_AMOUNT": 8, + "NULLIFIER_SILOING_AMOUNT": 8, + "ENCRYPTED_LOG_SILOING_AMOUNT": 2 } }, { @@ -40,7 +63,10 @@ "NOTE_HASH_SETTLED_AMOUNT": 4, "NULLIFIER_PENDING_AMOUNT": 4, "NULLIFIER_SETTLED_AMOUNT": 4, - "NULLIFIER_KEYS": 4 + "NULLIFIER_KEYS": 4, + "NOTE_HASH_SILOING_AMOUNT": 4, + "NULLIFIER_SILOING_AMOUNT": 4, + "ENCRYPTED_LOG_SILOING_AMOUNT": 1 } } -] \ No newline at end of file +] diff --git a/yarn-project/bb-prover/src/stats.ts b/yarn-project/bb-prover/src/stats.ts index e7f3da9302a9..ca0fa1ba9ca7 100644 --- a/yarn-project/bb-prover/src/stats.ts +++ b/yarn-project/bb-prover/src/stats.ts @@ -35,6 +35,8 @@ export function mapProtocolArtifactNameToCircuitName( return 'private-kernel-tail-to-public'; case 'PrivateKernelResetFullArtifact': return 'private-kernel-reset-full'; + case 'PrivateKernelResetFullInnerArtifact': + return 'private-kernel-reset-full-inner'; case 'PrivateKernelResetBigArtifact': return 'private-kernel-reset-big'; case 'PrivateKernelResetMediumArtifact': diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index b94647e0948d..8bf3fa858973 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -80,6 +80,7 @@ export type CircuitName = | 'private-kernel-init' | 'private-kernel-inner' | 'private-kernel-reset-full' + | 'private-kernel-reset-full-inner' | 'private-kernel-reset-big' | 'private-kernel-reset-medium' | 'private-kernel-reset-small' diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index ebb9ccc2e458..bd052e744f62 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -60,10 +60,11 @@ export const NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP = 16; export const PRIVATE_KERNEL_INIT_INDEX = 0; export const PRIVATE_KERNEL_INNER_INDEX = 1; export const PRIVATE_KERNEL_RESET_FULL_INDEX = 2; -export const PRIVATE_KERNEL_RESET_BIG_INDEX = 3; -export const PRIVATE_KERNEL_RESET_MEDIUM_INDEX = 4; -export const PRIVATE_KERNEL_RESET_SMALL_INDEX = 5; -export const PRIVATE_KERNEL_RESET_TINY_INDEX = 6; +export const PRIVATE_KERNEL_RESET_FULL_INNER_INDEX = 3; +export const PRIVATE_KERNEL_RESET_BIG_INDEX = 4; +export const PRIVATE_KERNEL_RESET_MEDIUM_INDEX = 5; +export const PRIVATE_KERNEL_RESET_SMALL_INDEX = 6; +export const PRIVATE_KERNEL_RESET_TINY_INDEX = 7; export const PRIVATE_KERNEL_TAIL_INDEX = 10; export const PRIVATE_KERNEL_TAIL_TO_PUBLIC_INDEX = 11; export const EMPTY_NESTED_INDEX = 12; diff --git a/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts b/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts index 0de55dd2b8b4..901a116b48e2 100644 --- a/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts +++ b/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts @@ -2,8 +2,11 @@ import fs from 'fs'; import path from 'path'; import { + MAX_ENCRYPTED_LOGS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_NOTE_HASHES_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, + MAX_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, } from '../constants.gen.js'; @@ -16,15 +19,21 @@ interface ResetVariant { NULLIFIER_PENDING_AMOUNT: number; NULLIFIER_SETTLED_AMOUNT: number; NULLIFIER_KEYS: number; + NOTE_HASH_SILOING_AMOUNT: number; + NULLIFIER_SILOING_AMOUNT: number; + ENCRYPTED_LOG_SILOING_AMOUNT: number; }; } const prelude = ` // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. import { + MAX_ENCRYPTED_LOGS_PER_TX, + MAX_NOTE_HASHES_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, + MAX_NULLIFIERS_PER_TX, } from '../../constants.gen.js'; import type { PrivateKernelResetCircuitPrivateInputs } from './private_kernel_reset_circuit_private_inputs.js'; `; @@ -34,12 +43,15 @@ function buildPrivateResetVariantsObject(variants: ResetVariant[]): string { for (const variant of variants) { output += ` - ${variant.tag}: { + '${variant.tag}': { NOTE_HASH_PENDING_AMOUNT: ${variant.replacements.NOTE_HASH_PENDING_AMOUNT}, NOTE_HASH_SETTLED_AMOUNT: ${variant.replacements.NOTE_HASH_SETTLED_AMOUNT}, NULLIFIER_PENDING_AMOUNT: ${variant.replacements.NULLIFIER_PENDING_AMOUNT}, NULLIFIER_SETTLED_AMOUNT: ${variant.replacements.NULLIFIER_SETTLED_AMOUNT}, NULLIFIER_KEYS: ${variant.replacements.NULLIFIER_KEYS}, + NOTE_HASH_SILOING_AMOUNT: ${variant.replacements.NOTE_HASH_SILOING_AMOUNT}, + NULLIFIER_SILOING_AMOUNT: ${variant.replacements.NULLIFIER_SILOING_AMOUNT}, + ENCRYPTED_LOG_SILOING_AMOUNT: ${variant.replacements.ENCRYPTED_LOG_SILOING_AMOUNT}, },`; } @@ -50,6 +62,9 @@ function buildPrivateResetVariantsObject(variants: ResetVariant[]): string { NULLIFIER_PENDING_AMOUNT: MAX_NULLIFIER_READ_REQUESTS_PER_TX, NULLIFIER_SETTLED_AMOUNT: MAX_NULLIFIER_READ_REQUESTS_PER_TX, NULLIFIER_KEYS: MAX_KEY_VALIDATION_REQUESTS_PER_TX, + NOTE_HASH_SILOING_AMOUNT: MAX_NOTE_HASHES_PER_TX, + NULLIFIER_SILOING_AMOUNT: MAX_NULLIFIERS_PER_TX, + ENCRYPTED_LOG_SILOING_AMOUNT: MAX_ENCRYPTED_LOGS_PER_TX, }, } as const;\n`; return output; @@ -86,19 +101,28 @@ function buildPrivateResetVariantsType(variants: ResetVariant[]): string { function validateVariants(variants: ResetVariant[]) { for (const variant of variants) { if (variant.replacements.NOTE_HASH_PENDING_AMOUNT > MAX_NOTE_HASH_READ_REQUESTS_PER_TX) { - throw new Error(`NOTE_HASH_PENDING_AMOUNT must be less than ${MAX_NOTE_HASH_READ_REQUESTS_PER_TX}`); + throw new Error(`NOTE_HASH_PENDING_AMOUNT must be less than or equal to ${MAX_NOTE_HASH_READ_REQUESTS_PER_TX}`); } if (variant.replacements.NOTE_HASH_SETTLED_AMOUNT > MAX_NOTE_HASH_READ_REQUESTS_PER_TX) { - throw new Error(`NOTE_HASH_SETTLED_AMOUNT must be less than ${MAX_NOTE_HASH_READ_REQUESTS_PER_TX}`); + throw new Error(`NOTE_HASH_SETTLED_AMOUNT must be less than or equal to ${MAX_NOTE_HASH_READ_REQUESTS_PER_TX}`); } if (variant.replacements.NULLIFIER_PENDING_AMOUNT > MAX_NULLIFIER_READ_REQUESTS_PER_TX) { - throw new Error(`NULLIFIER_PENDING_AMOUNT must be less than ${MAX_NULLIFIER_READ_REQUESTS_PER_TX}`); + throw new Error(`NULLIFIER_PENDING_AMOUNT must be less than or equal to ${MAX_NULLIFIER_READ_REQUESTS_PER_TX}`); } if (variant.replacements.NULLIFIER_SETTLED_AMOUNT > MAX_NULLIFIER_READ_REQUESTS_PER_TX) { - throw new Error(`NULLIFIER_SETTLED_AMOUNT must be less than ${MAX_NULLIFIER_READ_REQUESTS_PER_TX}`); + throw new Error(`NULLIFIER_SETTLED_AMOUNT must be less than or equal to ${MAX_NULLIFIER_READ_REQUESTS_PER_TX}`); } if (variant.replacements.NULLIFIER_KEYS > MAX_KEY_VALIDATION_REQUESTS_PER_TX) { - throw new Error(`NULLIFIER_KEYS must be less than ${MAX_KEY_VALIDATION_REQUESTS_PER_TX}`); + throw new Error(`NULLIFIER_KEYS must be less than or equal to ${MAX_KEY_VALIDATION_REQUESTS_PER_TX}`); + } + if (variant.replacements.NOTE_HASH_SILOING_AMOUNT > MAX_NOTE_HASHES_PER_TX) { + throw new Error(`NOTE_HASH_SILOING_AMOUNT must be less than or equal to ${MAX_NOTE_HASHES_PER_TX}`); + } + if (variant.replacements.NULLIFIER_SILOING_AMOUNT > MAX_NULLIFIERS_PER_TX) { + throw new Error(`NULLIFIER_SILOING_AMOUNT must be less than or equal to ${MAX_NULLIFIERS_PER_TX}`); + } + if (variant.replacements.ENCRYPTED_LOG_SILOING_AMOUNT > MAX_ENCRYPTED_LOGS_PER_TX) { + throw new Error(`ENCRYPTED_LOG_SILOING_AMOUNT must be less than or equal to ${MAX_ENCRYPTED_LOGS_PER_TX}`); } } } diff --git a/yarn-project/noir-protocol-circuits-types/src/artifacts.ts b/yarn-project/noir-protocol-circuits-types/src/artifacts.ts index 6962596aea89..143b4bd8d915 100644 --- a/yarn-project/noir-protocol-circuits-types/src/artifacts.ts +++ b/yarn-project/noir-protocol-circuits-types/src/artifacts.ts @@ -40,6 +40,7 @@ import RootRollupJson from '../artifacts/rollup_root.json' assert { type: 'json' export type PrivateResetArtifacts = | 'PrivateKernelResetFullArtifact' + | 'PrivateKernelResetFullInnerArtifact' | 'PrivateKernelResetBigArtifact' | 'PrivateKernelResetMediumArtifact' | 'PrivateKernelResetSmallArtifact' @@ -47,6 +48,7 @@ export type PrivateResetArtifacts = export const PrivateResetTagToArtifactName: Record = { full: 'PrivateKernelResetFullArtifact', + 'full-inner': 'PrivateKernelResetFullInnerArtifact', big: 'PrivateKernelResetBigArtifact', medium: 'PrivateKernelResetMediumArtifact', small: 'PrivateKernelResetSmallArtifact', @@ -105,6 +107,7 @@ export const SimulatedServerCircuitArtifacts: Record = { PrivateKernelResetFullArtifact: PrivateKernelResetSimulatedJson as NoirCompiledCircuit, + PrivateKernelResetFullInnerArtifact: PrivateKernelResetSimulatedJson as NoirCompiledCircuit, PrivateKernelResetBigArtifact: PrivateKernelResetBigSimulatedJson as NoirCompiledCircuit, PrivateKernelResetMediumArtifact: PrivateKernelResetMediumSimulatedJson as NoirCompiledCircuit, PrivateKernelResetSmallArtifact: PrivateKernelResetSmallSimulatedJson as NoirCompiledCircuit, @@ -115,6 +118,7 @@ export const ClientCircuitArtifacts: Record { ); for (const variant of resetVariants) { - circuits.push(`${resetCircuit}_${variant.tag}`); + circuits.push(`${resetCircuit}_${snakeCase(variant.tag)}`); } try { diff --git a/yarn-project/noir-protocol-circuits-types/src/vks.ts b/yarn-project/noir-protocol-circuits-types/src/vks.ts index 93af8571a5c1..a6fcb7b19184 100644 --- a/yarn-project/noir-protocol-circuits-types/src/vks.ts +++ b/yarn-project/noir-protocol-circuits-types/src/vks.ts @@ -11,6 +11,7 @@ import { PRIVATE_KERNEL_INNER_INDEX, PRIVATE_KERNEL_RESET_BIG_INDEX, PRIVATE_KERNEL_RESET_FULL_INDEX, + PRIVATE_KERNEL_RESET_FULL_INNER_INDEX, PRIVATE_KERNEL_RESET_MEDIUM_INDEX, PRIVATE_KERNEL_RESET_SMALL_INDEX, PRIVATE_KERNEL_RESET_TINY_INDEX, @@ -38,6 +39,7 @@ import PrivateKernelInitVkJson from '../artifacts/keys/private_kernel_init.vk.da import PrivateKernelInnerVkJson from '../artifacts/keys/private_kernel_inner.vk.data.json' assert { type: 'json' }; import PrivateKernelResetFullVkJson from '../artifacts/keys/private_kernel_reset.vk.data.json' assert { type: 'json' }; import PrivateKernelResetBigVkJson from '../artifacts/keys/private_kernel_reset_big.vk.data.json' assert { type: 'json' }; +import PrivateKernelResetFullInnerVkJson from '../artifacts/keys/private_kernel_reset_full_inner.vk.data.json' assert { type: 'json' }; import PrivateKernelResetMediumVkJson from '../artifacts/keys/private_kernel_reset_medium.vk.data.json' assert { type: 'json' }; import PrivateKernelResetSmallVkJson from '../artifacts/keys/private_kernel_reset_small.vk.data.json' assert { type: 'json' }; import PrivateKernelResetTinyVkJson from '../artifacts/keys/private_kernel_reset_tiny.vk.data.json' assert { type: 'json' }; @@ -90,6 +92,7 @@ const ClientCircuitVks: Record = { PrivateKernelInitArtifact: keyJsonToVKData(PrivateKernelInitVkJson), PrivateKernelInnerArtifact: keyJsonToVKData(PrivateKernelInnerVkJson), PrivateKernelResetFullArtifact: keyJsonToVKData(PrivateKernelResetFullVkJson), + PrivateKernelResetFullInnerArtifact: keyJsonToVKData(PrivateKernelResetFullInnerVkJson), PrivateKernelResetBigArtifact: keyJsonToVKData(PrivateKernelResetBigVkJson), PrivateKernelResetMediumArtifact: keyJsonToVKData(PrivateKernelResetMediumVkJson), PrivateKernelResetSmallArtifact: keyJsonToVKData(PrivateKernelResetSmallVkJson), @@ -109,6 +112,7 @@ export const ProtocolCircuitVkIndexes: Record = { PrivateKernelInitArtifact: PRIVATE_KERNEL_INIT_INDEX, PrivateKernelInnerArtifact: PRIVATE_KERNEL_INNER_INDEX, PrivateKernelResetFullArtifact: PRIVATE_KERNEL_RESET_FULL_INDEX, + PrivateKernelResetFullInnerArtifact: PRIVATE_KERNEL_RESET_FULL_INNER_INDEX, PrivateKernelResetBigArtifact: PRIVATE_KERNEL_RESET_BIG_INDEX, PrivateKernelResetMediumArtifact: PRIVATE_KERNEL_RESET_MEDIUM_INDEX, PrivateKernelResetSmallArtifact: PRIVATE_KERNEL_RESET_SMALL_INDEX, diff --git a/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts b/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts index fa05d462d522..b6234411cb0a 100644 --- a/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts +++ b/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts @@ -21,6 +21,7 @@ import { buildNoteHashReadRequestHints, buildNullifierReadRequestHints, buildTransientDataHints, + countAccumulatedItems, getNonEmptyItems, } from '@aztec/circuits.js'; import { makeTuple } from '@aztec/foundation/array'; @@ -92,6 +93,7 @@ export async function buildPrivateKernelResetInputs( noteHashLeafIndexMap: Map, noteHashNullifierCounterMap: Map, validationRequestsSplitCounter: number, + shouldSilo: boolean, oracle: ProvingDataOracle, ) { const publicInputs = previousKernelData.publicInputs; @@ -154,9 +156,11 @@ export async function buildPrivateKernelResetInputs( executionResult => executionResult.callStackItem.publicInputs.nullifierReadRequests, ); + const noteHashes = publicInputs.end.noteHashes; + const nullifiers = publicInputs.end.nullifiers; const [transientNullifierIndexesForNoteHashes, transientNoteHashIndexesForNullifiers] = buildTransientDataHints( - publicInputs.end.noteHashes, - publicInputs.end.nullifiers, + noteHashes, + nullifiers, futureNoteHashReads, futureNullifierReads, noteHashNullifierCounterMap, @@ -164,15 +168,36 @@ export async function buildPrivateKernelResetInputs( MAX_NULLIFIERS_PER_TX, ); + const numNoteHashes = countAccumulatedItems(noteHashes); + const remainingNoteHashes = transientNullifierIndexesForNoteHashes + .slice(0, numNoteHashes) + .reduce((index: number) => 0 + (index === nullifiers.length ? 1 : 0), 0); + + const numNullifiers = countAccumulatedItems(nullifiers); + const remainingNullifiers = transientNoteHashIndexesForNullifiers + .slice(0, numNullifiers) + .reduce((index: number) => 0 + (index === noteHashes.length ? 1 : 0), 0); + + const numEncryptedLogHashes = countAccumulatedItems(publicInputs.end.encryptedLogsHashes); + let privateInputs; for (const [sizeTag, hintSizes] of Object.entries(PRIVATE_RESET_VARIANTS)) { + const noSiloing = + !hintSizes.NOTE_HASH_SILOING_AMOUNT && + !hintSizes.NULLIFIER_SILOING_AMOUNT && + !hintSizes.ENCRYPTED_LOG_SILOING_AMOUNT; + const enoughSiloing = + hintSizes.NOTE_HASH_SILOING_AMOUNT >= remainingNoteHashes && + hintSizes.NULLIFIER_SILOING_AMOUNT >= remainingNullifiers && + hintSizes.ENCRYPTED_LOG_SILOING_AMOUNT >= numEncryptedLogHashes; if ( hintSizes.NOTE_HASH_PENDING_AMOUNT >= noteHashPendingReadHints && hintSizes.NOTE_HASH_SETTLED_AMOUNT >= noteHashSettledReadHints && hintSizes.NULLIFIER_PENDING_AMOUNT >= nullifierPendingReadHints && hintSizes.NULLIFIER_SETTLED_AMOUNT >= nullifierSettledReadHints && - hintSizes.NULLIFIER_KEYS >= keysCount + hintSizes.NULLIFIER_KEYS >= keysCount && + ((!shouldSilo && noSiloing) || (shouldSilo && enoughSiloing)) ) { privateInputs = new PrivateKernelResetCircuitPrivateInputs( previousKernelData, diff --git a/yarn-project/pxe/src/kernel_prover/hints/needs_reset.ts b/yarn-project/pxe/src/kernel_prover/hints/needs_reset.ts index c9eb7bc5f411..af66331152b3 100644 --- a/yarn-project/pxe/src/kernel_prover/hints/needs_reset.ts +++ b/yarn-project/pxe/src/kernel_prover/hints/needs_reset.ts @@ -6,30 +6,30 @@ import { MAX_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, type PrivateKernelCircuitPublicInputs, - getNonEmptyItems, + countAccumulatedItems, } from '@aztec/circuits.js'; import { type ExecutionResult } from '@aztec/simulator'; export function needsReset(publicInputs: PrivateKernelCircuitPublicInputs, executionStack: ExecutionResult[]) { const nextIteration = executionStack[executionStack.length - 1]; return ( - getNonEmptyItems(nextIteration.callStackItem.publicInputs.noteHashes).length + - getNonEmptyItems(publicInputs.end.noteHashes).length > + countAccumulatedItems(nextIteration.callStackItem.publicInputs.noteHashes) + + countAccumulatedItems(publicInputs.end.noteHashes) > MAX_NOTE_HASHES_PER_TX || - getNonEmptyItems(nextIteration.callStackItem.publicInputs.nullifiers).length + - getNonEmptyItems(publicInputs.end.nullifiers).length > + countAccumulatedItems(nextIteration.callStackItem.publicInputs.nullifiers) + + countAccumulatedItems(publicInputs.end.nullifiers) > MAX_NULLIFIERS_PER_TX || - getNonEmptyItems(nextIteration.callStackItem.publicInputs.noteEncryptedLogsHashes).length + - getNonEmptyItems(publicInputs.end.noteEncryptedLogsHashes).length > + countAccumulatedItems(nextIteration.callStackItem.publicInputs.noteEncryptedLogsHashes) + + countAccumulatedItems(publicInputs.end.noteEncryptedLogsHashes) > MAX_NOTE_ENCRYPTED_LOGS_PER_TX || - getNonEmptyItems(nextIteration.callStackItem.publicInputs.noteHashReadRequests).length + - getNonEmptyItems(publicInputs.validationRequests.noteHashReadRequests).length > + countAccumulatedItems(nextIteration.callStackItem.publicInputs.noteHashReadRequests) + + countAccumulatedItems(publicInputs.validationRequests.noteHashReadRequests) > MAX_NOTE_HASH_READ_REQUESTS_PER_TX || - getNonEmptyItems(nextIteration.callStackItem.publicInputs.nullifierReadRequests).length + - getNonEmptyItems(publicInputs.validationRequests.nullifierReadRequests).length > + countAccumulatedItems(nextIteration.callStackItem.publicInputs.nullifierReadRequests) + + countAccumulatedItems(publicInputs.validationRequests.nullifierReadRequests) > MAX_NULLIFIER_READ_REQUESTS_PER_TX || - getNonEmptyItems(nextIteration.callStackItem.publicInputs.keyValidationRequestsAndGenerators).length + - getNonEmptyItems(publicInputs.validationRequests.scopedKeyValidationRequestsAndGenerators).length > + countAccumulatedItems(nextIteration.callStackItem.publicInputs.keyValidationRequestsAndGenerators) + + countAccumulatedItems(publicInputs.validationRequests.scopedKeyValidationRequestsAndGenerators) > MAX_KEY_VALIDATION_REQUESTS_PER_TX ); } @@ -41,11 +41,14 @@ function hasTransientNullifier(publicInputs: PrivateKernelCircuitPublicInputs) { return publicInputs.end.nullifiers.some(n => noteHashSet.has(n.nullifiedNoteHash.toBigInt())); } -export function somethingToReset(publicInputs: PrivateKernelCircuitPublicInputs) { +export function needsFinalReset(publicInputs: PrivateKernelCircuitPublicInputs) { return ( - getNonEmptyItems(publicInputs.validationRequests.noteHashReadRequests).length > 0 || - getNonEmptyItems(publicInputs.validationRequests.nullifierReadRequests).length > 0 || - getNonEmptyItems(publicInputs.validationRequests.scopedKeyValidationRequestsAndGenerators).length > 0 || + countAccumulatedItems(publicInputs.end.noteHashes) > 0 || + countAccumulatedItems(publicInputs.end.nullifiers) > 0 || + countAccumulatedItems(publicInputs.end.encryptedLogsHashes) > 0 || + countAccumulatedItems(publicInputs.validationRequests.noteHashReadRequests) > 0 || + countAccumulatedItems(publicInputs.validationRequests.nullifierReadRequests) > 0 || + countAccumulatedItems(publicInputs.validationRequests.scopedKeyValidationRequestsAndGenerators) > 0 || hasTransientNullifier(publicInputs) ); } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index 26b4dfe05866..957edfe38821 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -31,7 +31,7 @@ import { import { type WitnessMap } from '@noir-lang/types'; -import { buildPrivateKernelResetInputs, needsReset, somethingToReset } from './hints/index.js'; +import { buildPrivateKernelResetInputs, needsFinalReset, needsReset } from './hints/index.js'; import { type ProvingDataOracle } from './proving_data_oracle.js'; const NULL_PROVE_OUTPUT: PrivateKernelSimulateOutput = { @@ -88,6 +88,7 @@ export class KernelProver { noteHashLeafIndexMap, noteHashNullifierCounterMap, validationRequestsSplitCounter, + false, ); output = await this.proofCreator.simulateProofReset(resetInputs); // TODO(#7368) consider refactoring this redundant bytecode pushing @@ -135,13 +136,14 @@ export class KernelProver { firstIteration = false; } - if (somethingToReset(output.publicInputs)) { + if (needsFinalReset(output.publicInputs)) { const resetInputs = await this.getPrivateKernelResetInputs( executionStack, output, noteHashLeafIndexMap, noteHashNullifierCounterMap, validationRequestsSplitCounter, + true, ); output = await this.proofCreator.simulateProofReset(resetInputs); // TODO(#7368) consider refactoring this redundant bytecode pushing @@ -188,6 +190,7 @@ export class KernelProver { noteHashLeafIndexMap: Map, noteHashNullifierCounterMap: Map, validationRequestsSplitCounter: number, + shouldSilo: boolean, ) { const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(output.verificationKey); const previousKernelData = new PrivateKernelData( @@ -203,6 +206,7 @@ export class KernelProver { noteHashLeafIndexMap, noteHashNullifierCounterMap, validationRequestsSplitCounter, + shouldSilo, this.oracle, ); } From 7f08fa9d3b4ca6e629935f06e23d0373cf244d37 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 9 Aug 2024 09:50:49 +0000 Subject: [PATCH 2/7] fmt and fix. --- .../private-kernel-lib/src/components/reset_output_composer.nr | 1 - .../src/utils/arrays/assert_sorted_transformed_value_array.nr | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr index 81f7e0596f34..6da82f388187 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer.nr @@ -93,7 +93,6 @@ impl ResetOutputComposer { let mut nullifiers = sort_by_counters_asc(self.hints.kept_nullifiers); for i in 0..nullifiers.len() { nullifiers[i].nullifier.value = silo_nullifier(nullifiers[i]); - nullifiers[i].nullifier.note_hash = 0; nullifiers[i].contract_address = AztecAddress::zero(); } nullifiers diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr index aa049308603f..ece3ccacc20c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr @@ -54,7 +54,7 @@ mod tests { } } - impl Eq for TestItem { + impl Eq for TestItem { fn eq(self, other: Self) -> bool { (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) } From 8404606189f8b43de7028947ac0738e4cb301436 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 9 Aug 2024 10:27:41 +0000 Subject: [PATCH 3/7] Fix hint. --- .../src/components/reset_output_composer/reset_output_hints.nr | 1 - 1 file changed, 1 deletion(-) diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr index 4895780486cd..61677cf85ba7 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr @@ -65,7 +65,6 @@ pub fn generate_reset_output_hints( let mut siloed_nullifiers = kept_nullifiers; for i in 0..kept_nullifiers.len() { siloed_nullifiers[i].nullifier.value = silo_nullifier(kept_nullifiers[i]); - siloed_nullifiers[i].nullifier.note_hash = 0; siloed_nullifiers[i].contract_address = AztecAddress::zero(); } let sorted_nullifier_hints = sort_get_order_hints_asc(kept_nullifiers); From bd41baf9cf75eab2323c483cd4ca2e5ed6f10203 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Fri, 9 Aug 2024 12:23:02 +0000 Subject: [PATCH 4/7] Update artifact. --- yarn-project/noir-protocol-circuits-types/src/artifacts.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yarn-project/noir-protocol-circuits-types/src/artifacts.ts b/yarn-project/noir-protocol-circuits-types/src/artifacts.ts index 143b4bd8d915..c87d1def6326 100644 --- a/yarn-project/noir-protocol-circuits-types/src/artifacts.ts +++ b/yarn-project/noir-protocol-circuits-types/src/artifacts.ts @@ -16,6 +16,7 @@ import PrivateKernelResetBigJson from '../artifacts/private_kernel_reset_big.jso import PrivateKernelResetMediumJson from '../artifacts/private_kernel_reset_medium.json' assert { type: 'json' }; import PrivateKernelResetSimulatedJson from '../artifacts/private_kernel_reset_simulated.json' assert { type: 'json' }; import PrivateKernelResetBigSimulatedJson from '../artifacts/private_kernel_reset_simulated_big.json' assert { type: 'json' }; +import PrivateKernelResetFullInnerSimulatedJson from '../artifacts/private_kernel_reset_simulated_full_inner.json' assert { type: 'json' }; import PrivateKernelResetMediumSimulatedJson from '../artifacts/private_kernel_reset_simulated_medium.json' assert { type: 'json' }; import PrivateKernelResetSmallSimulatedJson from '../artifacts/private_kernel_reset_simulated_small.json' assert { type: 'json' }; import PrivateKernelResetTinySimulatedJson from '../artifacts/private_kernel_reset_simulated_tiny.json' assert { type: 'json' }; @@ -107,7 +108,7 @@ export const SimulatedServerCircuitArtifacts: Record = { PrivateKernelResetFullArtifact: PrivateKernelResetSimulatedJson as NoirCompiledCircuit, - PrivateKernelResetFullInnerArtifact: PrivateKernelResetSimulatedJson as NoirCompiledCircuit, + PrivateKernelResetFullInnerArtifact: PrivateKernelResetFullInnerSimulatedJson as NoirCompiledCircuit, PrivateKernelResetBigArtifact: PrivateKernelResetBigSimulatedJson as NoirCompiledCircuit, PrivateKernelResetMediumArtifact: PrivateKernelResetMediumSimulatedJson as NoirCompiledCircuit, PrivateKernelResetSmallArtifact: PrivateKernelResetSmallSimulatedJson as NoirCompiledCircuit, From 1a5c8869951c70a41ae17a0bb302461f6938f53e Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Sat, 10 Aug 2024 09:20:55 +0000 Subject: [PATCH 5/7] Refactoring and adding tests. --- .../components/previous_kernel_validator.nr | 4 +- .../reset_output_hints.nr | 60 ++--- .../src/components/reset_output_validator.nr | 162 +++++------- .../src/components/tail_output_validator.nr | 15 +- .../tail_to_public_output_validator.nr | 11 +- .../src/private_kernel_reset.nr | 2 +- .../private-kernel-lib/src/tests/mod.nr | 1 + .../reset_output_validator_builder/mod.nr | 82 +++++++ .../validate_sorted_siloed_note_hashes.nr | 75 ++++++ .../validate_sorted_siloed_nullifiers.nr | 75 ++++++ .../validate_propagated_sorted_values.nr | 4 +- .../crates/types/src/utils/arrays.nr | 6 +- ..._exposed_sorted_transformed_value_array.nr | 232 ++++++++++++++++++ .../src/utils/arrays/assert_sorted_array.nr | 171 +------------ .../assert_sorted_transformed_value_array.nr | 186 ++++++++------ .../src/utils/arrays/sort_get_order_hints.nr | 10 +- 16 files changed, 683 insertions(+), 413 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_note_hashes.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_nullifiers.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr index c77beeb1649d..177fece92e40 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr @@ -88,8 +88,8 @@ impl PreviousKernelValidator { } fn verify_sorted_siloed_values(self) { - // Check that the following data are already siloed or sorted in the reset circuit. - // We only need to check the last non-empty item, since the reset circuit always processes from index 0. + // Check that the data are already siloed and/or sorted in the reset circuit. + // Any unprocessed data added after the last reset with siloing was called should be caught here. let num_note_hashes = array_length(self.previous_kernel.end.note_hashes); if num_note_hashes != 0 { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr index 61677cf85ba7..bc1e171be4f6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_composer/reset_output_hints.nr @@ -7,36 +7,29 @@ use crate::components::reset_output_composer::reset_output_hints::{ }; use dep::types::{ abis::{ - kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - log_hash::{NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, - nullifier::ScopedNullifier + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::NoteLogHash, + note_hash::ScopedNoteHash, nullifier::ScopedNullifier }, - address::AztecAddress, constants::{ MAX_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX }, - hash::{mask_encrypted_log_hash, silo_note_hash, silo_nullifier}, utils::arrays::{OrderHint, sort_get_order_hints_asc} }; struct ResetOutputHints { // note_hashes kept_note_hashes: [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX], - siloed_note_hashes: [ScopedNoteHash; MAX_NOTE_HASHES_PER_TX], - sorted_note_hash_hints: [OrderHint; MAX_NOTE_HASHES_PER_TX], + sorted_note_hash_indexes: [u32; MAX_NOTE_HASHES_PER_TX], // nullifiers kept_nullifiers: [ScopedNullifier; MAX_NULLIFIERS_PER_TX], - siloed_nullifiers: [ScopedNullifier; MAX_NULLIFIERS_PER_TX], - sorted_nullifier_hints: [OrderHint; MAX_NULLIFIERS_PER_TX], + sorted_nullifier_indexes: [u32; MAX_NULLIFIERS_PER_TX], // note_encrypted_log_hashes kept_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - exposed_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hash_hints: [OrderHint; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], + sorted_note_encrypted_log_hash_indexes: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], transient_or_propagated_note_hash_indexes_for_logs: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], // encrypted_log_hashes - masked_encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hash_hints: [OrderHint; MAX_ENCRYPTED_LOGS_PER_TX], + sorted_encrypted_log_hash_indexes: [u32; MAX_ENCRYPTED_LOGS_PER_TX], } pub fn generate_reset_output_hints( @@ -53,28 +46,13 @@ pub fn generate_reset_output_hints( ); // note_hashes - let mut siloed_note_hashes = kept_note_hashes; - let tx_hash = previous_kernel.end.nullifiers[0].value(); // First nullifier is tx hash. - let sorted_note_hash_hints = sort_get_order_hints_asc(kept_note_hashes); - for i in 0..kept_note_hashes.len() { - siloed_note_hashes[i].note_hash.value = silo_note_hash(kept_note_hashes[i], tx_hash, sorted_note_hash_hints[i].sorted_index); - siloed_note_hashes[i].contract_address = AztecAddress::zero(); - } + let sorted_note_hash_indexes = sort_get_order_hints_asc(kept_note_hashes).map(|h: OrderHint| h.sorted_index); // nullifiers - let mut siloed_nullifiers = kept_nullifiers; - for i in 0..kept_nullifiers.len() { - siloed_nullifiers[i].nullifier.value = silo_nullifier(kept_nullifiers[i]); - siloed_nullifiers[i].contract_address = AztecAddress::zero(); - } - let sorted_nullifier_hints = sort_get_order_hints_asc(kept_nullifiers); + let sorted_nullifier_indexes = sort_get_order_hints_asc(kept_nullifiers).map(|h: OrderHint| h.sorted_index); // note_encrypted_log_hashes - let mut exposed_note_encrypted_log_hashes = kept_note_encrypted_log_hashes; - for i in 0..exposed_note_encrypted_log_hashes.len() { - exposed_note_encrypted_log_hashes[i].note_hash_counter = 0; - } - let sorted_note_encrypted_log_hash_hints = sort_get_order_hints_asc(kept_note_encrypted_log_hashes); + let sorted_note_encrypted_log_hash_indexes = sort_get_order_hints_asc(kept_note_encrypted_log_hashes).map(|h: OrderHint| h.sorted_index); let transient_or_propagated_note_hash_indexes_for_logs = get_transient_or_propagated_note_hash_indexes_for_logs( previous_kernel.end.note_encrypted_logs_hashes, previous_kernel.end.note_hashes, @@ -82,26 +60,16 @@ pub fn generate_reset_output_hints( ); // encrypted_log_hashes - let encrypted_logs_hashes = previous_kernel.end.encrypted_logs_hashes; - let mut masked_encrypted_log_hashes = encrypted_logs_hashes; - for i in 0..masked_encrypted_log_hashes.len() { - masked_encrypted_log_hashes[i].contract_address = mask_encrypted_log_hash(encrypted_logs_hashes[i]); - masked_encrypted_log_hashes[i].log_hash.randomness = 0; - } - let sorted_encrypted_log_hash_hints = sort_get_order_hints_asc(previous_kernel.end.encrypted_logs_hashes); + let sorted_encrypted_log_hash_indexes = sort_get_order_hints_asc(previous_kernel.end.encrypted_logs_hashes).map(|h: OrderHint| h.sorted_index); ResetOutputHints { kept_note_hashes, - siloed_note_hashes, - sorted_note_hash_hints, + sorted_note_hash_indexes, kept_nullifiers, - siloed_nullifiers, - sorted_nullifier_hints, + sorted_nullifier_indexes, kept_note_encrypted_log_hashes, - exposed_note_encrypted_log_hashes, - sorted_note_encrypted_log_hash_hints, + sorted_note_encrypted_log_hash_indexes, transient_or_propagated_note_hash_indexes_for_logs, - masked_encrypted_log_hashes, - sorted_encrypted_log_hash_hints + sorted_encrypted_log_hash_indexes } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr index 64fc7137923c..2bac6e399bf0 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr @@ -3,11 +3,12 @@ use dep::reset_kernel_lib::verify_squashed_transient_data; use dep::types::{ abis::{ kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - log_hash::{NoteLogHash, ScopedEncryptedLogHash}, nullifier::ScopedNullifier + log_hash::{NoteLogHash, ScopedEncryptedLogHash}, note_hash::ScopedNoteHash, + nullifier::ScopedNullifier }, constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, hash::{mask_encrypted_log_hash, silo_note_hash, silo_nullifier}, traits::is_empty, - utils::arrays::assert_sorted_transformed_value_array + utils::arrays::{assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size} }; struct ResetOutputValidator { @@ -48,7 +49,18 @@ impl ResetOutputValidator { } pub fn validate(self) { - self.validate_squashed_data(); + verify_squashed_transient_data( + self.previous_kernel.end.note_hashes, + self.previous_kernel.end.nullifiers, + self.previous_kernel.end.note_encrypted_logs_hashes, + self.hints.kept_note_hashes, + self.hints.kept_nullifiers, + self.hints.kept_note_encrypted_log_hashes, + self.transient_nullifier_indexes_for_note_hashes, + self.transient_note_hash_indexes_for_nullifiers, + self.hints.transient_or_propagated_note_hash_indexes_for_logs, + self.split_counter + ); if self.note_hash_siloing_amount == 0 { assert_eq(self.output.note_hashes, self.hints.kept_note_hashes, "output note hashes mismatch"); @@ -75,21 +87,6 @@ impl ResetOutputValidator { } } - fn validate_squashed_data(self) { - verify_squashed_transient_data( - self.previous_kernel.end.note_hashes, - self.previous_kernel.end.nullifiers, - self.previous_kernel.end.note_encrypted_logs_hashes, - self.hints.kept_note_hashes, - self.hints.kept_nullifiers, - self.hints.kept_note_encrypted_log_hashes, - self.transient_nullifier_indexes_for_note_hashes, - self.transient_note_hash_indexes_for_nullifiers, - self.hints.transient_or_propagated_note_hash_indexes_for_logs, - self.split_counter - ); - } - fn validate_sorted_siloed_note_hashes(self) { // Check that the values are not already siloed in a previous reset. // Note hashes need to be siloed alltogether because new note hashes added later might affect the ordering and result in wrong nonces. @@ -100,73 +97,47 @@ impl ResetOutputValidator { is_empty(note_hash) | !note_hash.contract_address.is_zero(), "note hashes have been siloed in a previous reset" ); - // Don't have to check that note_hash_siloing_amount will cover all the non-empty note hashes. - // If it is not big enough, we won't be able to create a valid tail(_to_public) proof. - // It will fail in previous_kernel_validator > verify_sorted_siloed_values. - // Check siloing. let kept_note_hashes = self.hints.kept_note_hashes; - let siloed_note_hashes = self.hints.siloed_note_hashes; - let sorted_hints = self.hints.sorted_note_hash_hints; + let siloed_note_hashes = self.output.note_hashes; + let sorted_indexes = self.hints.sorted_note_hash_indexes; let tx_hash = self.output.nullifiers[0].value(); // First nullifier is tx hash. for i in 0..kept_note_hashes.len() { - let siloed_note_hash = siloed_note_hashes[i]; - let note_hash = kept_note_hashes[i]; if i < self.note_hash_siloing_amount { - let siloed_value = silo_note_hash(note_hash, tx_hash, sorted_hints[i].sorted_index); + let note_hash = kept_note_hashes[i]; + let sorted_index = sorted_indexes[i]; + let siloed_note_hash = siloed_note_hashes[sorted_index]; + let siloed_value = silo_note_hash(note_hash, tx_hash, sorted_index); assert_eq(siloed_note_hash.value(), siloed_value, "incorrect siloed note hashes"); - assert_eq( - siloed_note_hash.counter(), note_hash.counter(), "mismatch counter for siloed note hash" - ); - assert( - siloed_note_hash.contract_address.is_zero(), "contract address must not be exposed for siloed note hash" - ); } else { - // Don't have to check empty items in siloed_note_hash. - // assert_sorted_transformed_value_array ensures that there are the same amount of empty items in kept_note_hashes and in self.output.note_hashes. - // assert(is_empty(siloed_note_hash), "unexpected siloed note hash"); + // Don't have to check empty items here. + // assert_sorted_transformed_value_array_capped_size ensures that there are the same amount of empty items padded in kept_note_hashes and in self.output.note_hashes. } } // Check ordering. - assert_sorted_transformed_value_array( + assert_sorted_transformed_value_array_capped_size( kept_note_hashes, siloed_note_hashes, - self.output.note_hashes, - sorted_hints + |prev: ScopedNoteHash, out: ScopedNoteHash| out.contract_address.is_zero() & (out.counter() == prev.counter()), + sorted_indexes, + self.note_hash_siloing_amount ); } fn validate_sorted_siloed_nullifiers(self) { // Unlike note hashes, we don't have to check that the nullifiers haven't been siloed. // silo_nullifier() will return the already-siloed value if contract address is zero. - - // Check siloing. - let kept_nullifiers = self.hints.kept_nullifiers; - let siloed_nullifiers = self.hints.siloed_nullifiers; - let sorted_hints = self.hints.sorted_nullifier_hints; - for i in 0..kept_nullifiers.len() { - let siloed_nullifier = siloed_nullifiers[i]; - let nullifier = kept_nullifiers[i]; - if i < self.nullifier_siloing_amount { - let siloed_value = silo_nullifier(nullifier); - assert_eq(siloed_nullifier.value(), siloed_value, "incorrect siloed nullifier"); - assert_eq(siloed_nullifier.counter(), nullifier.counter(), "mismatch nullifier counter"); - assert_eq( - siloed_nullifier.nullifier.note_hash, nullifier.nullifier.note_hash, "mismatch nullified note hash" - ); - assert( - siloed_nullifier.contract_address.is_zero(), "contract address must not be exposed for siloed nullifier" - ); - } - } - - // Check ordering. - assert_sorted_transformed_value_array( - kept_nullifiers, - siloed_nullifiers, + assert_sorted_transformed_value_array_capped_size( + self.hints.kept_nullifiers, self.output.nullifiers, - sorted_hints + |prev: ScopedNullifier, out: ScopedNullifier| + (out.value() == silo_nullifier(prev)) & + (out.counter() == prev.counter()) & + (out.nullifier.note_hash == prev.nullifier.note_hash) & + out.contract_address.is_zero(), + self.hints.sorted_nullifier_indexes, + self.nullifier_siloing_amount ); } @@ -174,54 +145,41 @@ impl ResetOutputValidator { // This will be called together with validate_sorted_siloed_note_hashes(). // Same as validate_sorted_siloed_note_hashes, it will only be run once. // This is fine because we don't allow emitting logs for notes emitted in another function at the moment. + // All the note logs emitted in a function call must link to note hashes emitted in the same call. + // This is checked in PrivateCallDataValidator > validate_note_logs. - let kept_log_hashes = self.hints.kept_note_encrypted_log_hashes; - let exposed_log_hashes = self.hints.exposed_note_encrypted_log_hashes; - for i in 0..kept_log_hashes.len() { - let prev = kept_log_hashes[i]; - let out = exposed_log_hashes[i]; - assert_eq(out.value, prev.value, "mismatch note log value"); - assert_eq(out.length, prev.length, "mismatch note log length"); - assert_eq(out.counter, prev.counter, "mismatch note log counter"); - // The note_hash_counter was used when squashing note logs along with their corresponding note hashes. - // It won't be used later on, so we can set them to 0 here. - // It serves as a clue for the tail circuit to check that all the note logs are sorted in a reset circuit. - assert_eq(out.note_hash_counter, 0, "note_hash_counter should not be exposed for sorted note log"); - } + // note_hash_counter was used when squashing the note log along with its corresponding note hash. + // It won't be used later on, so we can set it to 0 here. + // It serves as a clue for the tail circuit to check that all the note logs are sorted in a reset circuit. + // This is not capped because we don't know how many logs there are. There can be any number of logs for each note hash. + // Consider adding a constant for it only when this becomes too costly. assert_sorted_transformed_value_array( - kept_log_hashes, - exposed_log_hashes, + self.hints.kept_note_encrypted_log_hashes, self.output.note_encrypted_log_hashes, - self.hints.sorted_note_encrypted_log_hash_hints + |prev: NoteLogHash, out: NoteLogHash| + (out.value == prev.value) & + (out.length == prev.length) & + (out.counter == prev.counter) & + (out.note_hash_counter == 0), + self.hints.sorted_note_encrypted_log_hash_indexes ); } fn validate_sorted_masked_encrypted_logs(self) { // Don't need to check that the logs are already masked. // If run repeatedly, it will return the masked contract address when randomness becomes 0. - - let log_hashes = self.previous_kernel.end.encrypted_logs_hashes; - let masked_log_hashes = self.hints.masked_encrypted_log_hashes; - for i in 0..log_hashes.len() { - if i < self.encrypted_log_siloing_amount { - let prev = log_hashes[i]; - let out = masked_log_hashes[i]; - assert_eq( - out.contract_address, mask_encrypted_log_hash(prev), "incorrect masked contract address" - ); - assert_eq(out.log_hash.value, prev.log_hash.value, "mismatch log value"); - assert_eq(out.log_hash.length, prev.log_hash.length, "mismatch log length"); - assert_eq(out.log_hash.counter, prev.log_hash.counter, "mismatch log counter"); - assert_eq(out.log_hash.randomness, 0, "randomness must be set to zero after masked"); - } - } - - assert_sorted_transformed_value_array( - log_hashes, - masked_log_hashes, + assert_sorted_transformed_value_array_capped_size( + self.previous_kernel.end.encrypted_logs_hashes, self.output.encrypted_log_hashes, - self.hints.sorted_encrypted_log_hash_hints + |prev: ScopedEncryptedLogHash, out: ScopedEncryptedLogHash| + (out.contract_address == mask_encrypted_log_hash(prev)) & + (out.log_hash.value == prev.log_hash.value) & + (out.log_hash.length == prev.log_hash.length) & + (out.log_hash.counter == prev.log_hash.counter) & + (out.log_hash.randomness == 0), + self.hints.sorted_encrypted_log_hash_indexes, + self.encrypted_log_siloing_amount ); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr index 8580be3f22ea..4787f401fb31 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr @@ -10,7 +10,7 @@ use dep::types::{ log_hash::{LogHash, NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash} }, messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::is_empty, - utils::arrays::{assert_sorted_transformed_value_array, assert_sorted_array_with_order_hints} + utils::arrays::assert_exposed_sorted_transformed_value_array }; struct TailOutputValidator { @@ -74,23 +74,20 @@ impl TailOutputValidator { fn validate_propagated_sorted_values(self) { // l2_to_l1_msgs - assert_sorted_transformed_value_array( + assert_exposed_sorted_transformed_value_array( self.previous_kernel.end.l2_to_l1_msgs, - self.previous_kernel.end.l2_to_l1_msgs.map(|log: ScopedL2ToL1Message| log.expose_to_public()), self.output.end.l2_to_l1_msgs, + |prev: ScopedL2ToL1Message, out: ScopedL2ToL1Message| out == prev.expose_to_public(), self.hints.sorted_l2_to_l1_msg_hints ); // unencrypted_log_hashes - assert_sorted_array_with_order_hints( + assert_exposed_sorted_transformed_value_array( self.previous_kernel.end.unencrypted_logs_hashes, - self.hints.sorted_unencrypted_log_hashes, + self.output.end.unencrypted_logs_hashes, + |prev: ScopedLogHash, out: ScopedLogHash| out == prev.expose_to_public(), self.hints.sorted_unencrypted_log_hash_hints ); - - assert_eq( - self.hints.sorted_unencrypted_log_hashes.map(|log: ScopedLogHash| log.expose_to_public()), self.output.end.unencrypted_logs_hashes, "mismatch unencrypted_logs_hashes" - ); } fn validate_gas_used(self) { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr index 6cef6663a9b3..1fd619af533d 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr @@ -10,7 +10,8 @@ use dep::types::{ log_hash::{LogHash, ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, nullifier::{Nullifier, ScopedNullifier}, public_call_request::PublicCallRequest }, - messaging::l2_to_l1_message::ScopedL2ToL1Message, address::AztecAddress, traits::is_empty_array, + messaging::l2_to_l1_message::ScopedL2ToL1Message, address::AztecAddress, + traits::{is_empty, is_empty_array}, utils::arrays::{ assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc, assert_split_transformed_value_arrays, validate_array @@ -69,16 +70,16 @@ impl TailToPublicOutputValidator { assert_eq(self.output.fee_payer, self.previous_kernel.fee_payer, "mismatch fee_payer"); // public_teardown_call_stack - let num_teardown_calls = validate_array(self.output.public_teardown_call_stack); - assert( - (num_teardown_calls == 0) | (num_teardown_calls == 1), "invalid number of teardown call requests" - ); assert_eq( self.output.public_teardown_call_stack[0].item, self.previous_kernel.public_teardown_call_request.item, "mismatch public_teardown_call_request" ); assert_eq( self.output.public_teardown_call_stack[0].counter, 0, "cannot expose teardown call request counter" ); + let teardown_call_stack = self.output.public_teardown_call_stack; + for i in 1..teardown_call_stack.len() { + assert(is_empty(teardown_call_stack[i]), "unexpected teardown call request"); + } } fn validate_propagated_split_values(self) { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr index 9ba7c2bb7626..499ccbf757bf 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -214,7 +214,6 @@ mod tests { let mut output = nullifiers; for i in 1..N { output[i].nullifier.value = silo_nullifier(nullifiers[i]); - output[i].nullifier.note_hash = 0; output[i].contract_address = AztecAddress::zero(); } output @@ -538,6 +537,7 @@ mod tests { builder.previous_kernel.append_note_hashes_with_logs(4); builder.previous_kernel.append_nullifiers(3); builder.previous_kernel.append_encrypted_log_hashes(3); + builder.previous_kernel.nullifiers.storage[2].nullifier.note_hash = 9988; // Get ordered items before shuffling. let note_hashes = builder.previous_kernel.note_hashes.storage; let nullifiers = builder.previous_kernel.nullifiers.storage; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/mod.nr index c400947751f5..75970221dbab 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/mod.nr @@ -2,6 +2,7 @@ mod previous_kernel_validator_builder; mod private_call_data_validator_builder; mod private_kernel_circuit_output_validator_builder; mod private_kernel_circuit_public_inputs_composer_builder; +mod reset_output_validator_builder; mod tail_output_composer_builder; mod tail_output_validator_builder; mod tail_to_public_output_composer_builder; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr new file mode 100644 index 000000000000..893d6de3e734 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/mod.nr @@ -0,0 +1,82 @@ +mod validate_sorted_siloed_note_hashes; +mod validate_sorted_siloed_nullifiers; + +use crate::components::{ + reset_output_composer::{PrivateKernelResetOutputs, reset_output_hints::{generate_reset_output_hints, ResetOutputHints}}, + reset_output_validator::ResetOutputValidator +}; +use dep::types::{constants::{MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX}, tests::fixture_builder::FixtureBuilder}; + +struct ResetOutputValidatorBuilder { + output: FixtureBuilder, + previous_kernel: FixtureBuilder, + transient_nullifier_indexes_for_note_hashes: [u32; MAX_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u32; MAX_NULLIFIERS_PER_TX], + split_counter: u32, + note_hash_siloing_amount: u32, + nullifier_siloing_amount: u32, + encrypted_log_siloing_amount: u32, +} + +impl ResetOutputValidatorBuilder { + pub fn new() -> Self { + let mut output = FixtureBuilder::new(); + let mut previous_kernel = FixtureBuilder::new(); + output.set_first_nullifier(); + previous_kernel.set_first_nullifier(); + + let transient_nullifier_indexes_for_note_hashes = [MAX_NULLIFIERS_PER_TX; MAX_NOTE_HASHES_PER_TX]; + let transient_note_hash_indexes_for_nullifiers = [MAX_NOTE_HASHES_PER_TX; MAX_NULLIFIERS_PER_TX]; + + ResetOutputValidatorBuilder { + output, + previous_kernel, + transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers, + split_counter: 0, + note_hash_siloing_amount: 0, + nullifier_siloing_amount: 0, + encrypted_log_siloing_amount: 0 + } + } + + pub fn get_hints(self) -> ResetOutputHints { + let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); + generate_reset_output_hints( + previous_kernel, + self.transient_nullifier_indexes_for_note_hashes, + self.transient_note_hash_indexes_for_nullifiers + ) + } + + pub fn get_output(self) -> PrivateKernelResetOutputs { + let data = self.output.to_private_accumulated_data(); + PrivateKernelResetOutputs { + note_hashes: data.note_hashes, + nullifiers: data.nullifiers, + note_encrypted_log_hashes: data.note_encrypted_logs_hashes, + encrypted_log_hashes: data.encrypted_logs_hashes + } + } + + pub fn validate_with_hints(self, hints: ResetOutputHints) { + let output = self.get_output(); + let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); + ResetOutputValidator::new( + output, + previous_kernel, + self.transient_nullifier_indexes_for_note_hashes, + self.transient_note_hash_indexes_for_nullifiers, + self.split_counter, + self.note_hash_siloing_amount, + self.nullifier_siloing_amount, + self.encrypted_log_siloing_amount, + hints + ).validate(); + } + + pub fn validate(self) { + let hints = self.get_hints(); + self.validate_with_hints(hints); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_note_hashes.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_note_hashes.nr new file mode 100644 index 000000000000..6c510b37292e --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_note_hashes.nr @@ -0,0 +1,75 @@ +use crate::tests::reset_output_validator_builder::ResetOutputValidatorBuilder; +use dep::types::tests::utils::swap_items; + +impl ResetOutputValidatorBuilder { + pub fn new_with_note_hash_siloing() -> Self { + let mut builder = ResetOutputValidatorBuilder::new(); + builder.note_hash_siloing_amount = 6; + builder + } +} + +#[test] +fn validate_sorted_siloed_note_hashes_succeeds() { + let mut builder = ResetOutputValidatorBuilder::new_with_note_hash_siloing(); + + builder.previous_kernel.append_note_hashes(4); + builder.output.append_siloed_note_hashes(4); + + builder.validate(); +} + +#[test] +fn validate_sorted_siloed_note_hashes_unordered_succeeds() { + let mut builder = ResetOutputValidatorBuilder::new_with_note_hash_siloing(); + + builder.previous_kernel.append_note_hashes(4); + swap_items(&mut builder.previous_kernel.note_hashes, 0, 2); + + builder.output.append_siloed_note_hashes(4); + + builder.validate(); +} + +#[test(should_fail_with="incorrect siloed note hashes")] +fn validate_sorted_siloed_note_hashes_mismatch_sorted_hash_fails() { + let mut builder = ResetOutputValidatorBuilder::new_with_note_hash_siloing(); + + builder.previous_kernel.append_note_hashes(2); + + builder.output.append_siloed_note_hashes(2); + // Swap the hashes in the output. + let tmp = builder.output.note_hashes.storage[0].note_hash.value; + builder.output.note_hashes.storage[0].note_hash.value = builder.output.note_hashes.storage[1].note_hash.value; + builder.output.note_hashes.storage[1].note_hash.value = tmp; + + builder.validate(); +} + +#[test(should_fail_with="unexpected non-empty item")] +fn validate_sorted_siloed_note_hashes_extra_item_fails() { + let mut builder = ResetOutputValidatorBuilder::new_with_note_hash_siloing(); + + builder.previous_kernel.append_note_hashes(2); + + // Append an extra item to the output. + builder.output.append_siloed_note_hashes(3); + let mut hints = builder.get_hints(); + // Tweak the hint to point to an empty item. + hints.sorted_note_hash_indexes[2] = 3; + + builder.validate_with_hints(hints); +} + +#[test(should_fail_with="note hashes have been siloed in a previous reset")] +fn validate_sorted_siloed_note_hashes_repeat_siloing_fails() { + let mut builder = ResetOutputValidatorBuilder::new_with_note_hash_siloing(); + + // Add a siloed note hash to the previous kernel. + builder.previous_kernel.append_siloed_note_hashes(1); + builder.previous_kernel.append_note_hashes(2); + + builder.output.append_siloed_note_hashes(3); + + builder.validate(); +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_nullifiers.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_nullifiers.nr new file mode 100644 index 000000000000..17bdc8b371a7 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/reset_output_validator_builder/validate_sorted_siloed_nullifiers.nr @@ -0,0 +1,75 @@ +use crate::tests::reset_output_validator_builder::ResetOutputValidatorBuilder; +use dep::types::tests::utils::swap_items; + +impl ResetOutputValidatorBuilder { + pub fn new_with_nullifier_siloing() -> Self { + let mut builder = ResetOutputValidatorBuilder::new(); + builder.nullifier_siloing_amount = 6; + builder + } +} + +#[test] +fn validate_sorted_siloed_nullifiers_succeeds() { + let mut builder = ResetOutputValidatorBuilder::new_with_nullifier_siloing(); + + builder.previous_kernel.append_nullifiers(3); + builder.output.append_siloed_nullifiers(3); + + builder.validate(); +} + +#[test] +fn validate_sorted_siloed_nullifiers_unordered_succeeds() { + let mut builder = ResetOutputValidatorBuilder::new_with_nullifier_siloing(); + + builder.previous_kernel.append_nullifiers(3); + swap_items(&mut builder.previous_kernel.nullifiers, 1, 2); + + builder.output.append_siloed_nullifiers(3); + + builder.validate(); +} + +#[test] +fn validate_sorted_siloed_nullifiers_repeatedly_succeeds() { + let mut builder = ResetOutputValidatorBuilder::new_with_nullifier_siloing(); + + // 2 nullifiers are already siloed. + builder.previous_kernel.append_siloed_nullifiers(2); + builder.previous_kernel.append_nullifiers(1); + + builder.output.append_siloed_nullifiers(3); + + builder.validate(); +} + +#[test(should_fail_with="incorrect transformed value")] +fn validate_sorted_siloed_nullifiers_mismatch_sorted_hash_fails() { + let mut builder = ResetOutputValidatorBuilder::new_with_nullifier_siloing(); + + builder.previous_kernel.append_nullifiers(3); + + builder.output.append_siloed_nullifiers(3); + // Swap the hashes in the output. + let tmp = builder.output.nullifiers.storage[0].nullifier.value; + builder.output.nullifiers.storage[0].nullifier.value = builder.output.nullifiers.storage[1].nullifier.value; + builder.output.nullifiers.storage[1].nullifier.value = tmp; + + builder.validate(); +} + +#[test(should_fail_with="unexpected non-empty item")] +fn validate_sorted_siloed_nullifiers_extra_item_fails() { + let mut builder = ResetOutputValidatorBuilder::new_with_nullifier_siloing(); + + builder.previous_kernel.append_nullifiers(2); + + // Append an extra item to the output. + builder.output.append_siloed_nullifiers(3); + let mut hints = builder.get_hints(); + // Tweak the hint to point to an empty item. + hints.sorted_nullifier_indexes[3] = 4; + + builder.validate_with_hints(hints); +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr index 8b539fb2dfda..beabe6d5a5db 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_values.nr @@ -26,7 +26,7 @@ fn validate_propagated_sorted_values_l2_to_l1_msgs_unordered_succeeds() { builder.validate(); } -#[test(should_fail_with="mismatch sorted values")] +#[test(should_fail_with="incorrect transformed value")] fn validate_propagated_sorted_values_l2_to_l1_msgs_mismatch_hash_fails() { let mut builder = TailOutputValidatorBuilder::new(); @@ -66,7 +66,7 @@ fn validate_propagated_sorted_values_unencrypted_log_hashes_unordered_succeeds() builder.validate(); } -#[test(should_fail_with="mismatch unencrypted_logs_hashes")] +#[test(should_fail_with="incorrect transformed value")] fn validate_propagated_sorted_values_unencrypted_log_hashes_wrong_order_fails() { let mut builder = TailOutputValidatorBuilder::new(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 700a31066f57..9356afa637c7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -1,3 +1,4 @@ +mod assert_exposed_sorted_transformed_value_array; mod assert_sorted_array; mod assert_sorted_transformed_value_array; mod assert_split_sorted_transformed_value_arrays; @@ -9,9 +10,10 @@ mod sort_get_sorted_tuple; mod sort_get_split_order_hints; // Re-exports. -use assert_sorted_array::{assert_sorted_array_with_order_hints, assert_sorted_array}; +use assert_exposed_sorted_transformed_value_array::assert_exposed_sorted_transformed_value_array; +use assert_sorted_array::assert_sorted_array; use assert_split_sorted_transformed_value_arrays::{assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc}; -use assert_sorted_transformed_value_array::assert_sorted_transformed_value_array; +use assert_sorted_transformed_value_array::{assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size}; use assert_split_transformed_value_arrays::assert_split_transformed_value_arrays; use sort_by_counters::{sort_by_counters_asc, sort_by_counters_desc}; use sort_get_order_hints::{OrderHint, sort_get_order_hints_asc, sort_get_order_hints_desc}; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr new file mode 100644 index 000000000000..0b8a4806c306 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_exposed_sorted_transformed_value_array.nr @@ -0,0 +1,232 @@ +use crate::{ + abis::side_effect::Ordered, traits::{Empty, is_empty}, + utils::arrays::{array_length, sort_get_order_hints::OrderHint} +}; + +// original_array must be valid, i.e. validate_array(original_array) == true +// Items in exposed_sorted_transformed_value_array do not have counters. Their corresponding counters are in hints. +pub fn assert_exposed_sorted_transformed_value_array( + original_array: [T; N], + exposed_sorted_transformed_value_array: [S; N], + is_transformed: fn[Env](T, S) -> bool, + hints: [OrderHint; N] +) where T: Ordered + Empty + Eq, S: Empty + Eq { + let num_non_empty_items = array_length(original_array); + let mut should_be_empty = false; + for i in 0..N { + should_be_empty |= i == num_non_empty_items; + if should_be_empty { + assert( + is_empty(exposed_sorted_transformed_value_array[i]), "array must be padded with empty items" + ); + } else { + let original = original_array[i]; + let sorted_index = hints[i].sorted_index; + let sorted = exposed_sorted_transformed_value_array[sorted_index]; + assert(is_transformed(original, sorted), "incorrect transformed value"); + assert_eq(original.counter(), hints[sorted_index].counter, "incorrect hinted counter"); + if i != 0 { + assert( + hints[i].counter > hints[i - 1].counter, "value array must be sorted by counter in ascending order" + ); + } + } + } +} + +mod tests { + use crate::{ + abis::side_effect::Ordered, traits::Empty, + utils::arrays::{ + assert_exposed_sorted_transformed_value_array::assert_exposed_sorted_transformed_value_array, + sort_get_order_hints::OrderHint + } + }; + + struct TestItem { + name: Field, + price: Field, + tax: Field, + counter: u32, + } + + impl Ordered for TestItem { + fn counter(self) -> u32 { + self.counter + } + } + + impl Empty for TestItem { + fn empty() -> Self { + TestItem { name: 0, price: 0, tax: 0, counter: 0 } + } + } + + impl Eq for TestItem { + fn eq(self, other: Self) -> bool { + (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) + } + } + + struct TestValue { + name: Field, + total: Field, + } + + impl Empty for TestValue { + fn empty() -> Self { + TestValue { name: 0, total: 0 } + } + } + + impl Eq for TestValue { + fn eq(self, other: Self) -> bool { + (self.name == other.name) & (self.total == other.total) + } + } + + fn is_transformed(item: TestItem, value: TestValue) -> bool { + (item.name == value.name) & ((item.price + item.tax) == value.total) + } + + struct TestDataBuilder { + original_array: [T; N], + exposed_sorted_transformed_value_array: [S; N], + hints: [OrderHint; N], + } + + impl TestDataBuilder { + pub fn new() -> Self { + let original_array = [ + TestItem { name: 100, price: 10, tax: 5, counter: 44 }, + TestItem { name: 200, price: 20, tax: 6, counter: 22 }, + TestItem { name: 300, price: 30, tax: 7, counter: 11 }, + TestItem { name: 400, price: 40, tax: 8, counter: 33 }, + TestItem::empty(), + TestItem::empty() + ]; + + let exposed_sorted_transformed_value_array = [ + TestValue { name: 300, total: 37 }, + TestValue { name: 200, total: 26 }, + TestValue { name: 400, total: 48 }, + TestValue { name: 100, total: 15 }, + TestValue::empty(), + TestValue::empty() + ]; + + let hints = [ + OrderHint { counter: 11, sorted_index: 3 }, + OrderHint { counter: 22, sorted_index: 1 }, + OrderHint { counter: 33, sorted_index: 0 }, + OrderHint { counter: 44, sorted_index: 2 }, + OrderHint { counter: 0, sorted_index: 4 }, + OrderHint { counter: 0, sorted_index: 5 } + ]; + + TestDataBuilder { original_array, exposed_sorted_transformed_value_array, hints } + } + + pub fn execute(self) { + assert_exposed_sorted_transformed_value_array( + self.original_array, + self.exposed_sorted_transformed_value_array, + is_transformed, + self.hints + ); + } + } + + #[test] + fn assert_exposed_sorted_transformed_value_array_succeeds() { + let builder = TestDataBuilder::new(); + builder.execute(); + } + + #[test(should_fail_with="incorrect transformed value")] + fn assert_exposed_sorted_transformed_value_array_mismatch_value_fails() { + let mut builder = TestDataBuilder::new(); + + // Tweak the value at index 1. + builder.exposed_sorted_transformed_value_array[1].total += 1; + + builder.execute(); + } + + #[test(should_fail_with="value array must be sorted by counter in ascending order")] + fn assert_exposed_sorted_transformed_value_array_unordered_fails() { + let mut builder = TestDataBuilder::new(); + + // Swap the values at index 1 and 2. + let tmp = builder.exposed_sorted_transformed_value_array[1]; + builder.exposed_sorted_transformed_value_array[1] = builder.exposed_sorted_transformed_value_array[2]; + builder.exposed_sorted_transformed_value_array[2] = tmp; + + // Update counters in hints. + let tmp = builder.hints[1].counter; + builder.hints[1].counter = builder.hints[2].counter; + builder.hints[2].counter = tmp; + + // Update sorted indexes. + // Original: 44, 22, 11, 33 + // New: 11, 33, 22, 44 + builder.hints[0].sorted_index = 3; + builder.hints[1].sorted_index = 2; + builder.hints[2].sorted_index = 0; + builder.hints[3].sorted_index = 1; + + builder.execute(); + } + + #[test(should_fail_with="incorrect hinted counter")] + fn assert_exposed_sorted_transformed_value_array_unordered_values_with_ordered_counters_fails() { + let mut builder = TestDataBuilder::new(); + + // Swap the values at index 1 and 2. + let tmp = builder.exposed_sorted_transformed_value_array[1]; + builder.exposed_sorted_transformed_value_array[1] = builder.exposed_sorted_transformed_value_array[2]; + builder.exposed_sorted_transformed_value_array[2] = tmp; + + // Update sorted indexes. + // Original: 44, 22, 11, 33 + // New: 11, 33, 22, 44 + builder.hints[0].sorted_index = 3; + builder.hints[1].sorted_index = 2; + builder.hints[2].sorted_index = 0; + builder.hints[3].sorted_index = 1; + + builder.execute(); + } + + #[test(should_fail_with="array must be padded with empty items")] + fn assert_exposed_sorted_transformed_value_array_extra_non_empty_fails() { + let mut builder = TestDataBuilder::new(); + + // Add a random item. + builder.exposed_sorted_transformed_value_array[4] = TestValue { name: 500, total: 10 }; + + builder.execute(); + } + + #[test(should_fail_with="array must be padded with empty items")] + fn assert_exposed_sorted_transformed_value_array_hint_to_extra_non_empty_fails() { + let mut builder = TestDataBuilder::new(); + + // Add a random item. + builder.exposed_sorted_transformed_value_array[4] = TestValue { name: 500, total: 10 }; + // Change the hint to point to an empty item. + builder.hints[4].sorted_index = 5; + + builder.execute(); + } + + #[test(should_fail_with="incorrect transformed value")] + fn assert_exposed_sorted_transformed_value_array_missing_item_fails() { + let mut builder = TestDataBuilder::new(); + + // Remove an item. + builder.exposed_sorted_transformed_value_array[3] = TestValue::empty(); + + builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_array.nr index 5485c6eafe31..3699e9e113a6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_array.nr @@ -1,4 +1,4 @@ -use crate::{abis::side_effect::Ordered, traits::{Empty, is_empty}, utils::arrays::sort_get_order_hints::OrderHint}; +use crate::traits::{Empty, is_empty}; pub fn assert_sorted_array( original_array: [T; N], @@ -24,36 +24,8 @@ pub fn assert_sorted_array( } } -// original_array must be valid, i.e. validate_array(original_array) == true -pub fn assert_sorted_array_with_order_hints( - original_array: [T; N], - sorted_array: [T; N], - hints: [OrderHint; N] -) where T: Ordered + Empty + Eq { - for i in 0..N { - let original_value = original_array[i]; - if is_empty(original_value) { - assert(is_empty(sorted_array[i]), "Empty values must not be mixed with sorted values"); - } else { - let hint = hints[i]; - assert_eq(sorted_array[hint.sorted_index], original_value, "Mismatch sorted values"); - if i != 0 { - assert( - sorted_array[i].counter() > sorted_array[i - 1].counter(), "Array must be sorted by counter in ascending order" - ); - } - } - } -} - mod tests { - use crate::{ - abis::side_effect::Ordered, traits::Empty, - utils::arrays::{ - assert_sorted_array::{assert_sorted_array, assert_sorted_array_with_order_hints}, - sort_get_order_hints::OrderHint - } - }; + use crate::utils::arrays::assert_sorted_array::assert_sorted_array; #[test] fn assert_sorted_array_custom_asc_success() { @@ -118,144 +90,5 @@ mod tests { let indexes = [1, 0, 2, 0, 0, 0]; assert_sorted_array(original, sorted, indexes, |a: Field, b: Field| a.lt(b)); } - - struct TestItem { - name: Field, - price: Field, - tax: Field, - counter: u32, - } - - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - - impl Empty for TestItem { - fn empty() -> Self { - TestItem { name: 0, price: 0, tax: 0, counter: 0 } - } - } - - impl Eq for TestItem { - fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.price == other.price) & (self.tax == other.tax) & (self.counter == other.counter) - } - } - - struct TestDataBuilder { - original_array: [T; N], - sorted_array: [T; N], - hints: [OrderHint; N], - } - - impl TestDataBuilder { - pub fn new() -> Self { - let original_array = [ - TestItem { name: 100, price: 10, tax: 5, counter: 44 }, - TestItem { name: 200, price: 20, tax: 6, counter: 22 }, - TestItem { name: 300, price: 30, tax: 7, counter: 11 }, - TestItem { name: 400, price: 40, tax: 8, counter: 33 }, - TestItem::empty(), - TestItem::empty() - ]; - - let sorted_array = [ - TestItem { name: 300, price: 30, tax: 7, counter: 11 }, - TestItem { name: 200, price: 20, tax: 6, counter: 22 }, - TestItem { name: 400, price: 40, tax: 8, counter: 33 }, - TestItem { name: 100, price: 10, tax: 5, counter: 44 }, - TestItem::empty(), - TestItem::empty() - ]; - - let hints = [ - OrderHint { counter: 11, sorted_index: 3 }, - OrderHint { counter: 22, sorted_index: 1 }, - OrderHint { counter: 33, sorted_index: 0 }, - OrderHint { counter: 44, sorted_index: 2 }, - OrderHint { counter: 0, sorted_index: 0 }, - OrderHint { counter: 0, sorted_index: 0 } - ]; - - TestDataBuilder { original_array, sorted_array, hints } - } - - pub fn execute(self) { - assert_sorted_array_with_order_hints(self.original_array, self.sorted_array, self.hints); - } - } - - #[test] - fn assert_sorted_array_with_order_hints_succeeds() { - let builder = TestDataBuilder::new(); - builder.execute(); - } - - #[test(should_fail_with="Mismatch sorted values")] - fn assert_sorted_array_with_order_hints_mismatch_value_fails() { - let mut builder = TestDataBuilder::new(); - - // Tweak the value at index 1. - builder.sorted_array[1].price += 1; - - builder.execute(); - } - - #[test(should_fail_with="Mismatch sorted values")] - fn assert_sorted_array_with_order_hints_mismatch_counter_fails() { - let mut builder = TestDataBuilder::new(); - - // Tweak the counter at index 1. - builder.sorted_array[1].counter += 1; - - builder.execute(); - } - - #[test(should_fail_with="Array must be sorted by counter in ascending order")] - fn assert_sorted_array_with_order_hints_unordered_fails() { - let mut builder = TestDataBuilder::new(); - - // Swap the values at index 1 and 2. - let tmp = builder.sorted_array[1]; - builder.sorted_array[1] = builder.sorted_array[2]; - builder.sorted_array[2] = tmp; - - // Update counters in hints. - let tmp = builder.hints[1].counter; - builder.hints[1].counter = builder.hints[2].counter; - builder.hints[2].counter = tmp; - - // Update sorted indexes. - // Original: 44, 22, 11, 33 - // New: 11, 33, 22, 44 - builder.hints[0].sorted_index = 3; - builder.hints[1].sorted_index = 2; - builder.hints[2].sorted_index = 0; - builder.hints[3].sorted_index = 1; - - builder.execute(); - } - - #[test(should_fail_with="Empty values must not be mixed with sorted values")] - fn assert_sorted_array_with_order_hints_extra_non_empty_fails() { - let mut builder = TestDataBuilder::new(); - - // Add a random item. - builder.sorted_array[4] = TestItem { name: 500, price: 10, tax: 5, counter: 55 }; - - builder.execute(); - } - - #[test(should_fail_with="Mismatch sorted values")] - fn assert_sorted_array_with_order_hints_missing_item_fails() { - let mut builder = TestDataBuilder::new(); - - // Remove an item. - builder.sorted_array[3] = TestItem::empty(); - - builder.execute(); - } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr index ece3ccacc20c..c85f82b3ed6e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_sorted_transformed_value_array.nr @@ -1,27 +1,52 @@ -use crate::{abis::side_effect::Ordered, traits::{Empty, is_empty}, utils::arrays::sort_get_order_hints::OrderHint}; +use crate::{abis::side_effect::Ordered, traits::{Empty, is_empty}, utils::arrays::array_length}; + +pub fn assert_sorted_transformed_value_array( + original_array: [T; N], + sorted_transformed_value_array: [S; N], + is_transformed: fn[Env](T, S) -> bool, + sorted_indexes: [u32; N] +) where T: Eq + Empty, S: Ordered + Eq + Empty { + assert_sorted_transformed_value_array_capped_size( + original_array, + sorted_transformed_value_array, + is_transformed, + sorted_indexes, + N + ); +} // original_array must be valid, i.e. validate_array(original_array) == true -// transformed_value_array must be verified against original_array before calling this function. -pub fn assert_sorted_transformed_value_array( +// capped_size should be a known constant at compile time. +pub fn assert_sorted_transformed_value_array_capped_size( original_array: [T; N], - transformed_value_array: [S; N], sorted_transformed_value_array: [S; N], - hints: [OrderHint; N] -) where T: Ordered + Empty + Eq, S: Empty + Eq { + is_transformed: fn[Env](T, S) -> bool, + sorted_indexes: [u32; N], + capped_size: u32 +) where T: Eq + Empty, S: Ordered + Eq + Empty { + let num_non_empty_items = array_length(original_array); + let mut should_be_empty = false; for i in 0..N { let original = original_array[i]; - if is_empty(original) { - assert(is_empty(sorted_transformed_value_array[i]), "unexpected non-empty item"); - } else { - let value = transformed_value_array[i]; - let sorted_index = hints[i].sorted_index; - assert_eq(value, sorted_transformed_value_array[sorted_index], "mismatch sorted values"); - assert_eq(original.counter(), hints[sorted_index].counter, "mismatch counters"); - if i != 0 { + let mapped_item = sorted_transformed_value_array[i]; + let is_mapped_item_empty = is_empty(mapped_item); + if i == capped_size { + assert(is_empty(original), "capped_size does not cover all non-empty items"); + } + if i < capped_size { + should_be_empty |= i == num_non_empty_items; + let sorted_index = sorted_indexes[i]; + let sorted = sorted_transformed_value_array[sorted_index]; + assert(is_transformed(original, sorted), "incorrect transformed value"); + if should_be_empty { + assert(is_mapped_item_empty, "unexpected non-empty item"); + } else if i != 0 { assert( - hints[i].counter > hints[i - 1].counter, "value array must be sorted by counter in ascending order" + mapped_item.counter() > sorted_transformed_value_array[i - 1].counter(), "value array must be sorted by counter in ascending order" ); } + } else { + assert(is_mapped_item_empty, "array must be padded with empty items"); } } } @@ -29,10 +54,7 @@ pub fn assert_sorted_transformed_value_array( mod tests { use crate::{ abis::side_effect::Ordered, traits::Empty, - utils::arrays::{ - assert_sorted_transformed_value_array::assert_sorted_transformed_value_array, - sort_get_order_hints::OrderHint - } + utils::arrays::{assert_sorted_transformed_value_array::{assert_sorted_transformed_value_array, assert_sorted_transformed_value_array_capped_size}} }; struct TestItem { @@ -42,12 +64,6 @@ mod tests { counter: u32, } - impl Ordered for TestItem { - fn counter(self) -> u32 { - self.counter - } - } - impl Empty for TestItem { fn empty() -> Self { TestItem { name: 0, price: 0, tax: 0, counter: 0 } @@ -63,29 +79,37 @@ mod tests { struct TestValue { name: Field, total: Field, + counter: u32, + } + + impl Ordered for TestValue { + fn counter(self) -> u32 { + self.counter + } } impl Empty for TestValue { fn empty() -> Self { - TestValue { name: 0, total: 0 } + TestValue { name: 0, total: 0, counter: 0 } } } impl Eq for TestValue { fn eq(self, other: Self) -> bool { - (self.name == other.name) & (self.total == other.total) + (self.name == other.name) & (self.total == other.total) & (self.counter == other.counter) } } - fn transform_value(item: TestItem) -> TestValue { - TestValue { name: item.name, total: item.price + item.tax } + fn is_transformed(item: TestItem, value: TestValue) -> bool { + (item.name == value.name) + & ((item.price + item.tax) == value.total) + & (item.counter == value.counter) } struct TestDataBuilder { original_array: [T; N], - transformed_value_array: [S; N], sorted_transformed_value_array: [S; N], - hints: [OrderHint; N], + sorted_indexes: [u32; N], } impl TestDataBuilder { @@ -99,35 +123,36 @@ mod tests { TestItem::empty() ]; - let transformed_value_array = original_array.map(|item: TestItem| transform_value(item)); - let sorted_transformed_value_array = [ - TestValue { name: 300, total: 37 }, - TestValue { name: 200, total: 26 }, - TestValue { name: 400, total: 48 }, - TestValue { name: 100, total: 15 }, + TestValue { name: 300, total: 37, counter: 11 }, + TestValue { name: 200, total: 26, counter: 22 }, + TestValue { name: 400, total: 48, counter: 33 }, + TestValue { name: 100, total: 15, counter: 44 }, TestValue::empty(), TestValue::empty() ]; - let hints = [ - OrderHint { counter: 11, sorted_index: 3 }, - OrderHint { counter: 22, sorted_index: 1 }, - OrderHint { counter: 33, sorted_index: 0 }, - OrderHint { counter: 44, sorted_index: 2 }, - OrderHint { counter: 0, sorted_index: 0 }, - OrderHint { counter: 0, sorted_index: 0 } - ]; + let sorted_indexes = [3, 1, 0, 2, 4, 5]; - TestDataBuilder { original_array, transformed_value_array, sorted_transformed_value_array, hints } + TestDataBuilder { original_array, sorted_transformed_value_array, sorted_indexes } } pub fn execute(self) { assert_sorted_transformed_value_array( self.original_array, - self.transformed_value_array, self.sorted_transformed_value_array, - self.hints + is_transformed, + self.sorted_indexes + ); + } + + pub fn execute_capped(self, capped_size: u32) { + assert_sorted_transformed_value_array_capped_size( + self.original_array, + self.sorted_transformed_value_array, + is_transformed, + self.sorted_indexes, + capped_size ); } } @@ -138,7 +163,7 @@ mod tests { builder.execute(); } - #[test(should_fail_with="mismatch sorted values")] + #[test(should_fail_with="incorrect transformed value")] fn assert_sorted_transformed_value_array_mismatch_value_fails() { let mut builder = TestDataBuilder::new(); @@ -148,16 +173,6 @@ mod tests { builder.execute(); } - #[test(should_fail_with="mismatch counters")] - fn assert_sorted_transformed_value_array_mismatch_counter_fails() { - let mut builder = TestDataBuilder::new(); - - // Tweak the counter at index 1. - builder.hints[1].counter += 1; - - builder.execute(); - } - #[test(should_fail_with="value array must be sorted by counter in ascending order")] fn assert_sorted_transformed_value_array_unordered_fails() { let mut builder = TestDataBuilder::new(); @@ -167,33 +182,40 @@ mod tests { builder.sorted_transformed_value_array[1] = builder.sorted_transformed_value_array[2]; builder.sorted_transformed_value_array[2] = tmp; - // Update counters in hints. - let tmp = builder.hints[1].counter; - builder.hints[1].counter = builder.hints[2].counter; - builder.hints[2].counter = tmp; - // Update sorted indexes. // Original: 44, 22, 11, 33 // New: 11, 33, 22, 44 - builder.hints[0].sorted_index = 3; - builder.hints[1].sorted_index = 2; - builder.hints[2].sorted_index = 0; - builder.hints[3].sorted_index = 1; + builder.sorted_indexes[0] = 3; + builder.sorted_indexes[1] = 2; + builder.sorted_indexes[2] = 0; + builder.sorted_indexes[3] = 1; builder.execute(); } - #[test(should_fail_with="unexpected non-empty item")] + #[test(should_fail_with="incorrect transformed value")] fn assert_sorted_transformed_value_array_extra_non_empty_fails() { let mut builder = TestDataBuilder::new(); // Add a random item. - builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10 }; + builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10, counter: 55 }; builder.execute(); } - #[test(should_fail_with="mismatch sorted values")] + #[test(should_fail_with="unexpected non-empty item")] + fn assert_sorted_transformed_value_array_hint_to_extra_non_empty_fails() { + let mut builder = TestDataBuilder::new(); + + // Add a random item. + builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10, counter: 55 }; + // Change the hint to point to an empty item. + builder.sorted_indexes[4] = 5; + + builder.execute(); + } + + #[test(should_fail_with="incorrect transformed value")] fn assert_sorted_transformed_value_array_missing_item_fails() { let mut builder = TestDataBuilder::new(); @@ -202,4 +224,26 @@ mod tests { builder.execute(); } + + #[test] + fn assert_sorted_transformed_value_array_capped_size_succeeds() { + let builder = TestDataBuilder::new(); + builder.execute_capped(5); + } + + #[test(should_fail_with="capped_size does not cover all non-empty items")] + fn assert_sorted_transformed_value_array_capped_size_not_fully_covered_fails() { + let builder = TestDataBuilder::new(); + builder.execute_capped(3); + } + + #[test(should_fail_with="array must be padded with empty items")] + fn assert_sorted_transformed_value_array_capped_size_extra_non_empty_fails() { + let mut builder = TestDataBuilder::new(); + + // Add a random item outside of capped_size. + builder.sorted_transformed_value_array[4] = TestValue { name: 500, total: 10, counter: 55 }; + + builder.execute_capped(4); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_order_hints.nr index bbf8d9f0fd1e..b60e5a500494 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_order_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_order_hints.nr @@ -36,6 +36,8 @@ pub fn sort_get_order_hints( if !is_empty(elem) { let original_index = sorted_tuples[i].original_index; hints[original_index].sorted_index = i; + } else { + hints[i].sorted_index = i; } } @@ -109,8 +111,8 @@ mod tests { OrderHint { counter: 3, sorted_index: 2 }, OrderHint { counter: 6, sorted_index: 0 }, OrderHint { counter: 9, sorted_index: 1 }, - OrderHint { counter: 0, sorted_index: 0 }, - OrderHint { counter: 0, sorted_index: 0 } + OrderHint { counter: 0, sorted_index: 3 }, + OrderHint { counter: 0, sorted_index: 4 } ]; assert_eq(hints, expected_hints); } @@ -145,8 +147,8 @@ mod tests { OrderHint { counter: 9, sorted_index: 0 }, OrderHint { counter: 6, sorted_index: 2 }, OrderHint { counter: 3, sorted_index: 1 }, - OrderHint { counter: 0, sorted_index: 0 }, - OrderHint { counter: 0, sorted_index: 0 } + OrderHint { counter: 0, sorted_index: 3 }, + OrderHint { counter: 0, sorted_index: 4 } ]; assert_eq(hints, expected_hints); } From ace632d2d0838e1a5df1b640ad18ddf4d21f3e62 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Sat, 10 Aug 2024 09:47:09 +0000 Subject: [PATCH 6/7] fmt --- .../crates/types/src/utils/arrays.nr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr index 9356afa637c7..5202c3700097 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays.nr @@ -34,10 +34,7 @@ pub fn array_to_bounded_vec(array: [T; N]) -> BoundedVec wh BoundedVec { storage: array, len } } -unconstrained fn filter_array_to_bounded_vec_unsafe( - arr: [T; N], - should_propagate: [bool; N] -) -> BoundedVec { +unconstrained fn filter_array_to_bounded_vec_unsafe(arr: [T; N], should_propagate: [bool; N]) -> BoundedVec { let mut vec = BoundedVec::new(); for i in 0..N { if should_propagate[i] { @@ -47,7 +44,10 @@ unconstrained fn filter_array_to_bounded_vec_unsafe( vec } -pub fn filter_array_to_bounded_vec(arr: [T; N], should_propagate: [bool; N]) -> BoundedVec where T: Eq { +pub fn filter_array_to_bounded_vec( + arr: [T; N], + should_propagate: [bool; N] +) -> BoundedVec where T: Eq { let vec_hint = filter_array_to_bounded_vec_unsafe(arr, should_propagate); let mut verifying_index = 0; From 09a76641c648a285891bb7316b38b0bfe2bc7879 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Sat, 10 Aug 2024 18:55:51 +0000 Subject: [PATCH 7/7] Fix. --- .../kernel_prover/hints/build_private_kernel_reset_hints.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts b/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts index b6234411cb0a..cdedebae3736 100644 --- a/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts +++ b/yarn-project/pxe/src/kernel_prover/hints/build_private_kernel_reset_hints.ts @@ -171,12 +171,12 @@ export async function buildPrivateKernelResetInputs( const numNoteHashes = countAccumulatedItems(noteHashes); const remainingNoteHashes = transientNullifierIndexesForNoteHashes .slice(0, numNoteHashes) - .reduce((index: number) => 0 + (index === nullifiers.length ? 1 : 0), 0); + .reduce((remaining, index) => remaining + (index === nullifiers.length ? 1 : 0), 0); const numNullifiers = countAccumulatedItems(nullifiers); const remainingNullifiers = transientNoteHashIndexesForNullifiers .slice(0, numNullifiers) - .reduce((index: number) => 0 + (index === noteHashes.length ? 1 : 0), 0); + .reduce((remaining, index) => remaining + (index === noteHashes.length ? 1 : 0), 0); const numEncryptedLogHashes = countAccumulatedItems(publicInputs.end.encryptedLogsHashes);