diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components.nr index c98c3b34595..b61366e753c 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components.nr @@ -1,7 +1,8 @@ -mod kernel_circuit_output_hints; -mod kernel_circuit_output_validator; -mod kernel_circuit_public_inputs_composer; mod previous_kernel_validator; mod private_call_data_validator; mod private_kernel_circuit_output_validator; mod private_kernel_circuit_public_inputs_composer; +mod tail_output_composer; +mod tail_output_validator; +mod tail_to_public_output_composer; +mod tail_to_public_output_validator; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_public_inputs_composer.nr deleted file mode 100644 index 6e2fc4d4c0e..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_public_inputs_composer.nr +++ /dev/null @@ -1,254 +0,0 @@ -use dep::types::{ - abis::{ - private_kernel_data::PrivateKernelData, - kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder, PublicKernelCircuitPublicInputs}, - note_hash::ScopedNoteHash, nullifier::ScopedNullifier, side_effect::Ordered, - log_hash::{NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash}, gas::Gas, call_request::CallRequest -}, - constants::{ - MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX -}, - hash::{ - silo_encrypted_log_hash, silo_l2_to_l1_message, silo_note_hash, silo_nullifier, - silo_unencrypted_log_hash -}, - utils::arrays::{array_to_bounded_vec, assert_sorted_array} -}; - -fn asc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { - a.counter() < b.counter() -} - -fn desc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { - a.counter() > b.counter() -} - -// Builds: -// .finish -> KernelCircuitPublicInputs (from PrivateKernelTailCircuitPrivateInputs) -// .finish_to_public -> PublicKernelCircuitPublicInputs (from PrivateKernelTailToPublicCircuitPrivateInputs) -struct KernelCircuitPublicInputsComposer { - public_inputs: PrivateKernelCircuitPublicInputsBuilder, - previous_kernel: PrivateKernelData, - // Hints - sorted_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_note_hashes_indexes: [u32; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], - sorted_nullifiers_indexes: [u32; MAX_NEW_NULLIFIERS_PER_TX], - sorted_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hashes_indexes: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes_indexes: [u32; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes_indexes: [u32; MAX_UNENCRYPTED_LOGS_PER_TX], -} - -impl KernelCircuitPublicInputsComposer { - pub fn new( - previous_kernel: PrivateKernelData, - sorted_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_note_hashes_indexes: [u32; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], - sorted_nullifiers_indexes: [u32; MAX_NEW_NULLIFIERS_PER_TX], - sorted_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hashes_indexes: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes_indexes: [u32; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes_indexes: [u32; MAX_UNENCRYPTED_LOGS_PER_TX] - ) -> Self { - let public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty(); - - KernelCircuitPublicInputsComposer { - public_inputs, - previous_kernel, - sorted_note_hashes, - sorted_note_hashes_indexes, - sorted_nullifiers, - sorted_nullifiers_indexes, - sorted_note_encrypted_log_hashes, - sorted_note_encrypted_log_hashes_indexes, - sorted_encrypted_log_hashes, - sorted_encrypted_log_hashes_indexes, - sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hashes_indexes - } - } - - pub fn compose(&mut self) -> Self { - self.propagate_rollup_validation_requests(); - - self.propagate_constant_data(); - - self.propagate_sorted_arrays(); - - self.propagate_fee_payer(); - - self.silo_values(); - - *self - } - - pub fn compose_public( - &mut self, - sorted_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], - sorted_call_requests_indexes: [u32; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX] - ) -> Self { - let _ = self.compose(); - - self.propagate_sorted_public_call_requests(sorted_call_requests, sorted_call_requests_indexes); - self.propagate_public_teardown_call_request(); - - *self - } - - pub fn finish(self) -> KernelCircuitPublicInputs { - let teardown_gas = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.teardown_gas_limits; - let inputs = self.public_inputs.finish_tail(teardown_gas); - let limits = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.gas_limits; - assert(inputs.end.gas_used.within(limits), "The gas used exceeds the gas limits"); - inputs - } - - pub fn finish_to_public(self) -> PublicKernelCircuitPublicInputs { - let min_revertible_side_effect_counter = self.previous_kernel.public_inputs.min_revertible_side_effect_counter; - let teardown_gas = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.teardown_gas_limits; - let inputs = self.public_inputs.finish_to_public(teardown_gas, min_revertible_side_effect_counter); - let limits = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.gas_limits; - let total_gas_used = inputs.end.gas_used + inputs.end_non_revertible.gas_used; - assert(total_gas_used.within(limits), "The gas used exceeds the gas limits"); - inputs - } - - fn silo_values(&mut self) { - self.silo_note_hashes(); - self.silo_nullifiers(); - self.silo_l2_to_l1_messages(); - self.silo_encrypted_logs(); - self.silo_unencrypted_logs(); - } - - fn silo_note_hashes(&mut self) { - // First nullifier is tx hash. - let tx_hash = self.public_inputs.end.new_nullifiers.get_unchecked(0).value(); - - let note_hashes = self.public_inputs.end.new_note_hashes.storage; - for i in 0..note_hashes.len() { - self.public_inputs.end.new_note_hashes.storage[i].note_hash.value = silo_note_hash( - note_hashes[i], - tx_hash, - i - ); - } - } - - fn silo_nullifiers(&mut self) { - let nullifiers = self.public_inputs.end.new_nullifiers.storage; - for i in 1..nullifiers.len() { // i starts from 1 to skip the first nullifier. - self.public_inputs.end.new_nullifiers.storage[i].nullifier.value = silo_nullifier(nullifiers[i]); - } - } - - fn silo_l2_to_l1_messages(&mut self) { - let l2_to_l1_msgs = self.public_inputs.end.new_l2_to_l1_msgs.storage; - let tx_context = self.previous_kernel.public_inputs.constants.tx_context; - for i in 0..l2_to_l1_msgs.len() { - self.public_inputs.end.new_l2_to_l1_msgs.storage[i].message.content = silo_l2_to_l1_message( - l2_to_l1_msgs[i], - tx_context.version, - tx_context.chain_id, - ); - } - } - - fn silo_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].log_hash.value = silo_encrypted_log_hash(logs[i]); - } - } - - fn silo_unencrypted_logs(&mut self) { - let logs = self.public_inputs.end.unencrypted_logs_hashes.storage; - for i in 0..logs.len() { - self.public_inputs.end.unencrypted_logs_hashes.storage[i].log_hash.value = silo_unencrypted_log_hash(logs[i]); - } - } - - fn propagate_rollup_validation_requests(&mut self) { - self.public_inputs.validation_requests.max_block_number = self.previous_kernel.public_inputs.validation_requests.for_rollup.max_block_number; - } - - fn propagate_constant_data(&mut self) { - self.public_inputs.constants = self.previous_kernel.public_inputs.constants; - } - - fn propagate_sorted_arrays(&mut self) { - let accumulated_data = self.previous_kernel.public_inputs.end; - - assert_sorted_array( - accumulated_data.new_note_hashes, - self.sorted_note_hashes, - self.sorted_note_hashes_indexes, - asc_sort_by_counters - ); - self.public_inputs.end.new_note_hashes = array_to_bounded_vec(self.sorted_note_hashes); - - assert_sorted_array( - accumulated_data.new_nullifiers, - self.sorted_nullifiers, - self.sorted_nullifiers_indexes, - asc_sort_by_counters - ); - self.public_inputs.end.new_nullifiers = array_to_bounded_vec(self.sorted_nullifiers); - - assert_sorted_array( - accumulated_data.note_encrypted_logs_hashes, - self.sorted_note_encrypted_log_hashes, - self.sorted_note_encrypted_log_hashes_indexes, - asc_sort_by_counters - ); - self.public_inputs.end.note_encrypted_logs_hashes = array_to_bounded_vec(self.sorted_note_encrypted_log_hashes); - - assert_sorted_array( - accumulated_data.encrypted_logs_hashes, - self.sorted_encrypted_log_hashes, - self.sorted_encrypted_log_hashes_indexes, - asc_sort_by_counters - ); - self.public_inputs.end.encrypted_logs_hashes = array_to_bounded_vec(self.sorted_encrypted_log_hashes); - - assert_sorted_array( - accumulated_data.unencrypted_logs_hashes, - self.sorted_unencrypted_log_hashes, - self.sorted_unencrypted_log_hashes_indexes, - asc_sort_by_counters - ); - self.public_inputs.end.unencrypted_logs_hashes = array_to_bounded_vec(self.sorted_unencrypted_log_hashes); - // TODO: Sort all the side effects below. - self.public_inputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(accumulated_data.new_l2_to_l1_msgs); - } - - fn propagate_sorted_public_call_requests( - &mut self, - sorted_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], - sorted_call_requests_indexes: [u32; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX] - ) { - let accumulated_data = self.previous_kernel.public_inputs.end; - assert_sorted_array( - accumulated_data.public_call_stack, - sorted_call_requests, - sorted_call_requests_indexes, - desc_sort_by_counters - ); - self.public_inputs.end.public_call_stack = array_to_bounded_vec(sorted_call_requests); - } - - fn propagate_public_teardown_call_request(&mut self) { - self.public_inputs.public_teardown_call_request = self.previous_kernel.public_inputs.public_teardown_call_request; - } - - fn propagate_fee_payer(&mut self) { - self.public_inputs.fee_payer = self.previous_kernel.public_inputs.fee_payer; - } -} 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 d0b0bb5959e..27d7ce6d667 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 @@ -13,15 +13,17 @@ impl PreviousKernelValidator { } pub fn validate_for_private_tail(self) { - self.validate_empty_private_call_stack(); + self.validate_common(); self.validate_empty_public_call_stack(); - self.verify_empty_validation_requests(); - self.verify_no_transient_data(); } pub fn validate_for_private_tail_to_public(self) { - self.validate_empty_private_call_stack(); + self.validate_common(); self.validate_non_empty_public_call_stack(); + } + + fn validate_common(self) { + self.validate_empty_private_call_stack(); self.verify_empty_validation_requests(); self.verify_no_transient_data(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator.nr index 8ff5b188043..5e0d9e6eee3 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator.nr @@ -1,10 +1,17 @@ +mod find_first_revertible_item_index; +mod validate_split_ranges; + +use crate::components::private_call_data_validator::{ + find_first_revertible_item_index::find_first_revertible_item_index, + validate_split_ranges::validate_split_ranges +}; use dep::types::{ abis::{ call_context::CallContext, call_request::CallRequest, caller_context::CallerContext, kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, log_hash::NoteLogHash, note_hash::ScopedNoteHash, private_call_request::ScopedPrivateCallRequest, private_call_stack_item::PrivateCallStackItem, - private_circuit_public_inputs::PrivateCircuitPublicInputsArrayLengths, + private_circuit_public_inputs::{PrivateCircuitPublicInputs, PrivateCircuitPublicInputsArrayLengths}, private_kernel::private_call_data::PrivateCallData, side_effect::{Ordered, RangeOrdered} }, address::{AztecAddress, PartialAddress}, contract_class_id::ContractClassId, @@ -12,13 +19,23 @@ use dep::types::{ traits::is_empty, transaction::tx_request::TxRequest, utils::arrays::find_index }; -unconstrained fn match_log_to_note(note_log: NoteLogHash, accumulated_note_hashes: [ScopedNoteHash; N]) -> u32 { +unconstrained fn match_log_to_note( + note_log: NoteLogHash, + accumulated_note_hashes: [ScopedNoteHash; N] +) -> u32 { find_index( accumulated_note_hashes, |n: ScopedNoteHash| n.counter() == note_log.note_hash_counter ) } +unconstrained fn find_first_revertible_private_call_request_index(public_inputs: PrivateCircuitPublicInputs) -> u32 { + find_first_revertible_item_index( + public_inputs.min_revertible_side_effect_counter, + public_inputs.private_call_requests + ) +} + fn validate_caller_context(caller_context: CallerContext, this_context: CallContext) { let matching_caller_context = caller_context.msg_sender.eq(this_context.msg_sender) & caller_context.storage_contract_address.eq(this_context.storage_contract_address); @@ -38,12 +55,7 @@ fn validate_call_request(request: CallRequest, hash: Field, caller: PrivateCallS } } -fn validate_incrementing_counters_within_range( - counter_start: u32, - counter_end: u32, - items: [T; N], - num_items: u32 -) where T: Ordered { +fn validate_incrementing_counters_within_range(counter_start: u32, counter_end: u32, items: [T; N], num_items: u32) where T: Ordered { let mut prev_counter = counter_start; let mut should_check = true; for i in 0..N { @@ -83,27 +95,6 @@ fn validate_incrementing_counter_ranges_within_range( ); } -fn validate_clean_split_ranges( - min_revertible_side_effect_counter: u32, - first_revertible_item_index: u32, - items: [T; N], - num_items: u32 -) where T: RangeOrdered { - if first_revertible_item_index != 0 { - let last_non_revertible_item_index = first_revertible_item_index - 1; - let item = items[last_non_revertible_item_index]; - assert( - min_revertible_side_effect_counter > item.counter_end(), "min_revertible_side_effect_counter must be greater than the end counter of the last non revertible item" - ); - } - if first_revertible_item_index != num_items { - let item = items[first_revertible_item_index]; - assert( - min_revertible_side_effect_counter <= item.counter_start(), "min_revertible_side_effect_counter must be less than or equal to the start counter of the first revertible item" - ); - } -} - struct PrivateCallDataValidator { data: PrivateCallData, array_lengths: PrivateCircuitPublicInputsArrayLengths, @@ -125,18 +116,19 @@ impl PrivateCallDataValidator { self.validate_note_logs(accumulated_note_hashes); } - pub fn validate_as_first_call(self, first_revertible_private_call_request_index: u32) { + pub fn validate_as_first_call(self) { let public_inputs = self.data.call_stack_item.public_inputs; let call_context = public_inputs.call_context; assert(call_context.is_delegate_call == false, "Users cannot make a delegatecall"); assert(call_context.is_static_call == false, "Users cannot make a static call"); let min_revertible_side_effect_counter = public_inputs.min_revertible_side_effect_counter; + let first_revertible_index = find_first_revertible_private_call_request_index(public_inputs); // No need to check that the min_revertible_side_effect_counter falls in the counter range of the private call. // It is valid as long as it does not fall in the middle of any nested call. - validate_clean_split_ranges( + validate_split_ranges( min_revertible_side_effect_counter, - first_revertible_private_call_request_index, + first_revertible_index, public_inputs.private_call_requests, self.array_lengths.private_call_requests ); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/find_first_revertible_item_index.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/find_first_revertible_item_index.nr new file mode 100644 index 00000000000..81a87d7b206 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/find_first_revertible_item_index.nr @@ -0,0 +1,97 @@ +use dep::types::{abis::side_effect::Ordered, traits::Empty, utils::arrays::array_length}; + +pub fn find_first_revertible_item_index( + min_revertible_side_effect_counter: u32, + items: [T; N] +) -> u32 where T: Ordered + Empty + Eq { + let mut index = N; + for i in 0..N { + let item = items[i]; + if (index == N) & (item.counter() >= min_revertible_side_effect_counter) { + index = i; + } + } + if index == N { + index = array_length(items); + } + index +} + +mod tests { + use crate::components::private_call_data_validator::find_first_revertible_item_index::find_first_revertible_item_index; + use dep::types::tests::fixture_builder::FixtureBuilder; + + struct TestBuilder { + private_call: FixtureBuilder + } + + impl TestBuilder { + pub fn new() -> Self { + let private_call = FixtureBuilder::new(); + TestBuilder { private_call } + } + + pub fn execute(self) -> u32 { + let private_call = self.private_call.to_private_circuit_public_inputs(); + find_first_revertible_item_index( + private_call.min_revertible_side_effect_counter, + private_call.private_call_requests + ) + } + } + + #[test] + fn find_first_revertible_item_index_empty() { + let builder = TestBuilder::new(); + let index = builder.execute(); + assert_eq(index, 0); + } + + #[test] + fn find_first_revertible_item_index_empty_with_min_counter() { + let mut builder = TestBuilder::new(); + + builder.private_call.min_revertible_side_effect_counter = 5; + + let index = builder.execute(); + assert_eq(index, 0); + } + + #[test] + fn find_first_revertible_item_index_only_revertible() { + let mut builder = TestBuilder::new(); + + // Revertible. + builder.private_call.end_setup(); + builder.private_call.append_private_call_requests(3); + + let index = builder.execute(); + assert_eq(index, 0); + } + + #[test] + fn find_first_revertible_item_index_only_non_revertible() { + let mut builder = TestBuilder::new(); + + // Non-revertible. + builder.private_call.append_private_call_requests(2); + builder.private_call.end_setup(); + + let index = builder.execute(); + assert_eq(index, 2); + } + + #[test] + fn find_first_revertible_item_index_both() { + let mut builder = TestBuilder::new(); + + // Non-revertible. + builder.private_call.append_private_call_requests(2); + // Revertible. + builder.private_call.end_setup(); + builder.private_call.append_private_call_requests(3); + + let index = builder.execute(); + assert_eq(index, 2); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_split_ranges.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_split_ranges.nr new file mode 100644 index 00000000000..6c50ff6c50c --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_split_ranges.nr @@ -0,0 +1,184 @@ +use dep::types::abis::side_effect::RangeOrdered; + +pub fn validate_split_ranges( + min_revertible_side_effect_counter: u32, + first_revertible_item_index: u32, + items: [T; N], + num_items: u32 +) where T: RangeOrdered { + if first_revertible_item_index != 0 { + let last_non_revertible_item_index = first_revertible_item_index - 1; + let item = items[last_non_revertible_item_index]; + assert( + min_revertible_side_effect_counter > item.counter_end(), "min_revertible_side_effect_counter must be greater than the end counter of the last non revertible item" + ); + } + if first_revertible_item_index != num_items { + let item = items[first_revertible_item_index]; + assert( + min_revertible_side_effect_counter <= item.counter_start(), "min_revertible_side_effect_counter must be less than or equal to the start counter of the first revertible item" + ); + } +} + +mod tests { + use crate::components::private_call_data_validator::validate_split_ranges::validate_split_ranges; + use dep::types::tests::fixture_builder::FixtureBuilder; + + struct TestBuilder { + private_call: FixtureBuilder, + first_revertible_private_call_request_index: u32, + } + + impl TestBuilder { + pub fn new() -> Self { + let private_call = FixtureBuilder::new(); + TestBuilder { private_call, first_revertible_private_call_request_index: 0 } + } + + pub fn split_calls(&mut self, counter: u32) { + self.private_call.min_revertible_side_effect_counter = counter; + self.first_revertible_private_call_request_index = self.private_call.private_call_requests.len(); + } + + pub fn add_private_call_request(&mut self, counter_start: u32, counter_end: u32) { + let index = self.private_call.private_call_requests.len(); + self.private_call.append_private_call_requests(1); + self.private_call.private_call_requests.storage[index].call_request.start_side_effect_counter = counter_start; + self.private_call.private_call_requests.storage[index].call_request.end_side_effect_counter = counter_end; + self.private_call.counter = counter_end + 1; + } + + pub fn execute(self) { + validate_split_ranges( + self.private_call.min_revertible_side_effect_counter, + self.first_revertible_private_call_request_index, + self.private_call.private_call_requests.storage, + self.private_call.private_call_requests.len() + ); + } + } + + #[test] + fn validate_split_ranges_succeeds() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + builder.split_calls(60); + builder.add_private_call_request(60, 70); + + builder.execute(); + } + + #[test] + fn validate_split_ranges_empty_revertible_succeeds() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + builder.split_calls(51); + + builder.execute(); + } + + #[test] + fn validate_split_ranges_empty_non_revertible_succeeds() { + let mut builder = TestBuilder::new(); + + builder.split_calls(20); + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + + builder.execute(); + } + + #[test] + fn validate_split_ranges_less_than_first_revertible_success() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + // Tweak the counter to be less than the start counter of the first revertible call. + builder.split_calls(59); + builder.add_private_call_request(60, 70); + + builder.execute(); + } + + #[test(should_fail_with="min_revertible_side_effect_counter must be greater than the end counter of the last non revertible item")] + fn validate_split_ranges_less_than_last_non_revertible_fails() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + // Tweak the counter to be less than the end counter of the last non-revertible call. + builder.split_calls(49); + builder.add_private_call_request(60, 70); + + builder.execute(); + } + + #[test(should_fail_with="min_revertible_side_effect_counter must be greater than the end counter of the last non revertible item")] + fn validate_split_ranges_equal_last_non_revertible_fails() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + // Tweak the counter to equal the end counter of the last non-revertible call. + builder.split_calls(50); + + builder.execute(); + } + + #[test(should_fail_with="min_revertible_side_effect_counter must be less than or equal to the start counter of the first revertible item")] + fn validate_split_ranges_greater_than_first_revertible_fails() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + // Tweak the counter to be greater than the start counter of the first revertible call. + builder.split_calls(61); + builder.add_private_call_request(60, 70); + + builder.execute(); + } + + #[test] + fn validate_split_ranges_0_succeeds() { + let mut builder = TestBuilder::new(); + + // Set the counter to be 0. + builder.split_calls(0); + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + + builder.execute(); + } + + #[test(should_fail_with="min_revertible_side_effect_counter must be greater than the end counter of the last non revertible item")] + fn validate_split_ranges_0_wrong_hint_fails() { + let mut builder = TestBuilder::new(); + + builder.split_calls(0); + // Set the index hint to be 1. + builder.first_revertible_private_call_request_index = 1; + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + + builder.execute(); + } + + #[test(should_fail_with="min_revertible_side_effect_counter must be less than or equal to the start counter of the first revertible item")] + fn validate_split_ranges_index_hint_greater_than_len_fails() { + let mut builder = TestBuilder::new(); + + builder.add_private_call_request(20, 30); + builder.add_private_call_request(40, 50); + builder.split_calls(51); + // Increase the index by 1. + builder.first_revertible_private_call_request_index += 1; + + builder.execute(); + } +} 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 b11bf4e76fc..2a0c27b2393 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 @@ -9,9 +9,13 @@ use dep::types::{ constants::{ MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL +}, + hash::{ + silo_encrypted_log_hash, silo_l2_to_l1_message, silo_note_hash, silo_nullifier, + silo_unencrypted_log_hash }, traits::is_empty, transaction::tx_request::TxRequest, - utils::arrays::{array_length, array_to_bounded_vec} + utils::arrays::{array_length, array_to_bounded_vec, sort_by_counters_asc, sort_by_counters_desc} }; struct DataSource { @@ -72,13 +76,18 @@ impl PrivateKernelCircuitPublicInputsComposer { public_inputs.end.encrypted_logs_hashes = array_to_bounded_vec(start.encrypted_logs_hashes); public_inputs.end.unencrypted_logs_hashes = array_to_bounded_vec(start.unencrypted_logs_hashes); public_inputs.end.private_call_stack = array_to_bounded_vec(start.private_call_stack); - let _call_request = public_inputs.end.private_call_stack.pop(); public_inputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); PrivateKernelCircuitPublicInputsComposer { public_inputs } } - pub fn compose( + pub fn pop_top_call_request(&mut self) -> Self { + // Pop the top item in the call stack, which is the caller of the current call, and shouldn't be propagated to the output. + let _call_request = self.public_inputs.end.private_call_stack.pop(); + *self + } + + pub fn with_private_call( &mut self, private_call_public_inputs: PrivateCircuitPublicInputs, contract_address: AztecAddress, @@ -95,11 +104,17 @@ impl PrivateKernelCircuitPublicInputsComposer { public_call_requests, public_teardown_call_request }; + self.propagate_from_private_call(source); *self } + pub fn sort_and_silo(&mut self) { + self.sort_ordered_values(); + self.silo_scoped_values(); + } + pub fn finish(self) -> PrivateKernelCircuitPublicInputs { self.public_inputs.finish() } @@ -264,4 +279,67 @@ impl PrivateKernelCircuitPublicInputsComposer { self.public_inputs.fee_payer = source.storage_contract_address; } } + + fn sort_ordered_values(&mut self) { + self.public_inputs.end.new_note_hashes.storage = sort_by_counters_asc(self.public_inputs.end.new_note_hashes.storage); + self.public_inputs.end.new_nullifiers.storage = sort_by_counters_asc(self.public_inputs.end.new_nullifiers.storage); + self.public_inputs.end.new_l2_to_l1_msgs.storage = sort_by_counters_asc(self.public_inputs.end.new_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_stack.storage = sort_by_counters_desc(self.public_inputs.end.public_call_stack.storage); + } + + fn silo_scoped_values(&mut self) { + self.silo_note_hashes(); + self.silo_nullifiers(); + self.silo_l2_to_l1_messages(); + self.silo_encrypted_logs(); + self.silo_unencrypted_logs(); + } + + fn silo_note_hashes(&mut self) { + let first_nullifier = self.public_inputs.end.new_nullifiers.get_unchecked(0).value(); + let note_hashes = self.public_inputs.end.new_note_hashes.storage; + for i in 0..note_hashes.len() { + self.public_inputs.end.new_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.new_nullifiers.storage; + for i in 0..nullifiers.len() { + self.public_inputs.end.new_nullifiers.storage[i].nullifier.value = silo_nullifier(nullifiers[i]); + } + } + + fn silo_l2_to_l1_messages(&mut self) { + let l2_to_l1_msgs = self.public_inputs.end.new_l2_to_l1_msgs.storage; + let tx_context = self.public_inputs.constants.tx_context; + for i in 0..l2_to_l1_msgs.len() { + self.public_inputs.end.new_l2_to_l1_msgs.storage[i].message.content = silo_l2_to_l1_message( + l2_to_l1_msgs[i], + tx_context.version, + tx_context.chain_id, + ); + } + } + + fn silo_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].log_hash.value = silo_encrypted_log_hash(logs[i]); + } + } + + fn silo_unencrypted_logs(&mut self) { + let logs = self.public_inputs.end.unencrypted_logs_hashes.storage; + for i in 0..logs.len() { + self.public_inputs.end.unencrypted_logs_hashes.storage[i].log_hash.value = silo_unencrypted_log_hash(logs[i]); + } + } } 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 new file mode 100644 index 00000000000..955a000f6e4 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr @@ -0,0 +1,69 @@ +use crate::components::private_kernel_circuit_public_inputs_composer::PrivateKernelCircuitPublicInputsComposer; +use dep::types::{ + abis::{ + accumulated_data::combined_accumulated_data::CombinedAccumulatedData, gas::Gas, + kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs}, + log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, + nullifier::ScopedNullifier +}, + constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE}, + hash::{compute_tx_logs_hash, compute_tx_note_logs_hash}, + messaging::l2_to_l1_message::ScopedL2ToL1Message +}; + +struct TailOutputComposer { + output_composer: PrivateKernelCircuitPublicInputsComposer, +} + +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(); + + TailOutputComposer { output_composer } + } + + pub fn finish(self) -> KernelCircuitPublicInputs { + let source = self.output_composer.finish(); + let mut output = KernelCircuitPublicInputs::empty(); + output.rollup_validation_requests = source.validation_requests.for_rollup; + output.end = self.build_combined_accumulated_data(); + output.constants = source.constants; + output.fee_payer = source.fee_payer; + output + } + + fn build_combined_accumulated_data(self) -> CombinedAccumulatedData { + let source = self.output_composer.public_inputs.end; + let mut data = CombinedAccumulatedData::empty(); + data.new_note_hashes = source.new_note_hashes.storage.map(|n: ScopedNoteHash| n.note_hash.value); + data.new_nullifiers = source.new_nullifiers.storage.map(|n: ScopedNullifier| n.nullifier.value); + data.new_l2_to_l1_msgs = source.new_l2_to_l1_msgs.storage.map(|m: ScopedL2ToL1Message| m.message.content); + data.note_encrypted_logs_hash = compute_tx_note_logs_hash(source.note_encrypted_logs_hashes.storage.map(|l: NoteLogHash| l.expose_to_public())); + data.encrypted_logs_hash = compute_tx_logs_hash(source.encrypted_logs_hashes.storage.map(|l: ScopedEncryptedLogHash| l.expose_to_public())); + data.unencrypted_logs_hash = compute_tx_logs_hash(source.unencrypted_logs_hashes.storage.map(|l: ScopedLogHash| l.expose_to_public())); + data.note_encrypted_log_preimages_length = source.note_encrypted_logs_hashes.storage.fold(0, |len, l: NoteLogHash| len + l.length); + data.encrypted_log_preimages_length = source.encrypted_logs_hashes.storage.fold(0, |len, l: ScopedEncryptedLogHash| len + l.log_hash.length); + data.unencrypted_log_preimages_length = source.unencrypted_logs_hashes.storage.fold(0, |len, l: ScopedLogHash| len + l.log_hash.length); + data.gas_used = self.meter_gas_used(data); + data + } + + fn meter_gas_used(self, data: CombinedAccumulatedData) -> Gas { + let mut metered_bytes = 0; + + let data_builder = self.output_composer.public_inputs.end; + // IMPORTANT: Must use data_builder.__.len(), which is the the number of items pushed to the BoundedVec. + // Do not use data.__.len(), which is the array's max length. + metered_bytes += data_builder.new_note_hashes.len() * DA_BYTES_PER_FIELD; + metered_bytes += data_builder.new_nullifiers.len() * DA_BYTES_PER_FIELD; + metered_bytes += data_builder.new_l2_to_l1_msgs.len() * DA_BYTES_PER_FIELD; + + metered_bytes += data.note_encrypted_log_preimages_length as u32; + metered_bytes += data.encrypted_log_preimages_length as u32; + metered_bytes += data.unencrypted_log_preimages_length as u32; + + let teardown_gas = self.output_composer.public_inputs.constants.tx_context.gas_settings.teardown_gas_limits; + Gas::new(metered_bytes * DA_GAS_PER_BYTE, 0) + Gas::tx_overhead() + teardown_gas + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr similarity index 80% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_validator.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr index ee69034e025..add8346b798 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr @@ -1,57 +1,42 @@ -use crate::components::{kernel_circuit_output_hints::{Hints, OrderHint}}; +mod kernel_circuit_output_hints; +mod validate_value_transformation; + +use crate::components::tail_output_validator::{ + kernel_circuit_output_hints::{generate_kernel_circuit_output_hints, Hints}, + validate_value_transformation::{validate_transformed_values, validate_value_transformation} +}; use dep::types::{ abis::{ kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs}, log_hash::{LogHash, NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash} }, - constants::MAX_NEW_NOTE_HASHES_PER_TX, hash::{ compute_tx_logs_hash, compute_tx_note_logs_hash, silo_encrypted_log_hash, silo_l2_to_l1_message, silo_note_hash, silo_nullifier, silo_unencrypted_log_hash }, - traits::{Empty, is_empty}, utils::arrays::assert_sorted_transformed_value_array + traits::is_empty, utils::arrays::assert_sorted_transformed_value_array }; -fn validate_transformed_value_array( - 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"); - } -} - -fn validate_siloed_value_array( - original_array: [T; N], - transformed_value_array: [S; N], - silo_value: fn[Env](T) -> S -) where S: Empty + Eq { - validate_transformed_value_array( - original_array, - transformed_value_array, - |original: T, transformed: S| transformed == silo_value(original) - ); -} - -struct KernelCircuitOutputValidator { +struct TailOutputValidator { output: KernelCircuitPublicInputs, - previous_kernel: PrivateKernelCircuitPublicInputs + previous_kernel: PrivateKernelCircuitPublicInputs, } -impl KernelCircuitOutputValidator { +impl TailOutputValidator { pub fn new( output: KernelCircuitPublicInputs, previous_kernel: PrivateKernelCircuitPublicInputs ) -> Self { - KernelCircuitOutputValidator { output, previous_kernel } + TailOutputValidator { output, previous_kernel } } - pub fn validate(self, hints: Hints) { + pub fn validate(self) { + let hints = generate_kernel_circuit_output_hints(self.previous_kernel); self.validate_empty_values(); self.validate_propagated_values(); self.validate_propagated_sorted_siloed_values(hints); self.validate_accumulated_values(hints); + self.validate_gas_limits(); } fn validate_empty_values(self) { @@ -86,7 +71,7 @@ impl KernelCircuitOutputValidator { ); // new_nullifiers - validate_siloed_value_array( + validate_transformed_values( self.previous_kernel.end.new_nullifiers, hints.siloed_nullifiers, silo_nullifier @@ -101,7 +86,7 @@ impl KernelCircuitOutputValidator { // new_l2_to_l1_msgs let tx_context = self.previous_kernel.constants.tx_context; - validate_siloed_value_array( + validate_transformed_values( self.previous_kernel.end.new_l2_to_l1_msgs, hints.siloed_l2_to_l1_msgs, |msg| silo_l2_to_l1_message(msg, tx_context.version, tx_context.chain_id) @@ -117,7 +102,7 @@ impl KernelCircuitOutputValidator { fn validate_accumulated_values(self, hints: Hints) { // note_encrypted_log_hashes - validate_transformed_value_array( + validate_value_transformation( self.previous_kernel.end.note_encrypted_logs_hashes, hints.note_encrypted_log_hashes, |nlh: NoteLogHash, lh: LogHash| (nlh.value == lh.value) & (nlh.length == lh.length) @@ -134,7 +119,7 @@ impl KernelCircuitOutputValidator { assert_eq(hash, self.output.end.note_encrypted_logs_hash, "mismatch note_encrypted_logs_hash"); // encrypted_log_hashes - validate_transformed_value_array( + validate_value_transformation( self.previous_kernel.end.encrypted_logs_hashes, hints.siloed_encrypted_log_hashes, |slh: ScopedEncryptedLogHash, lh: LogHash| (lh.value == silo_encrypted_log_hash(slh)) & (lh.length == slh.log_hash.length) @@ -151,7 +136,7 @@ impl KernelCircuitOutputValidator { assert_eq(hash, self.output.end.encrypted_logs_hash, "mismatch encrypted_logs_hash"); // unencrypted_log_hashes - validate_transformed_value_array( + validate_value_transformation( self.previous_kernel.end.unencrypted_logs_hashes, hints.siloed_unencrypted_log_hashes, |slh: ScopedLogHash, lh: LogHash| (lh.value == silo_unencrypted_log_hash(slh)) & (lh.length == slh.log_hash.length) @@ -167,4 +152,9 @@ impl KernelCircuitOutputValidator { let hash = compute_tx_logs_hash(hints.sorted_siloed_unencrypted_log_hashes); assert_eq(hash, self.output.end.unencrypted_logs_hash, "mismatch unencrypted_logs_hash"); } + + fn validate_gas_limits(self) { + let limits = self.previous_kernel.constants.tx_context.gas_settings.gas_limits; + assert(self.output.end.gas_used.within(limits), "The gas used exceeds the gas limits"); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_hints.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/kernel_circuit_output_hints.nr similarity index 97% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_hints.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/kernel_circuit_output_hints.nr index 96d57d0783c..716147ac73b 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/kernel_circuit_output_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/kernel_circuit_output_hints.nr @@ -39,7 +39,7 @@ struct Hints { sorted_unencrypted_log_hash_hints: [OrderHint; MAX_UNENCRYPTED_LOGS_PER_TX], } -unconstrained pub fn generate_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> Hints { +unconstrained pub fn generate_kernel_circuit_output_hints(previous_kernel: PrivateKernelCircuitPublicInputs) -> Hints { // note_hashes let sorted_note_hash_hints = sort_get_order_hints_asc(previous_kernel.end.new_note_hashes); 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 new file mode 100644 index 00000000000..e7b8d392702 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_value_transformation.nr @@ -0,0 +1,23 @@ +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 new file mode 100644 index 00000000000..a51363f34b6 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr @@ -0,0 +1,42 @@ +mod meter_gas_used; +mod split_to_public; + +use crate::components::{ + private_kernel_circuit_public_inputs_composer::PrivateKernelCircuitPublicInputsComposer, + tail_to_public_output_composer::{ + meter_gas_used::{meter_gas_used_non_revertible, meter_gas_used_revertible}, + split_to_public::split_to_public +} +}; +use dep::types::abis::kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}; + +struct TailToPublicOutputComposer { + output_composer: PrivateKernelCircuitPublicInputsComposer, +} + +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(); + + TailToPublicOutputComposer { output_composer } + } + + pub fn finish(self) -> PublicKernelCircuitPublicInputs { + let source = self.output_composer.public_inputs; + let mut output = PublicKernelCircuitPublicInputs::empty(); + output.validation_requests = source.validation_requests.finish(); + output.constants = source.constants; + output.public_teardown_call_stack[0] = source.public_teardown_call_request; + output.fee_payer = source.fee_payer; + + let mut (end_non_revertible, end) = split_to_public(source.end, source.min_revertible_side_effect_counter); + end_non_revertible.gas_used = meter_gas_used_non_revertible(end_non_revertible); + let teardown_gas = source.constants.tx_context.gas_settings.teardown_gas_limits; + end.gas_used = meter_gas_used_revertible(end, teardown_gas); + output.end_non_revertible = end_non_revertible.finish(); + output.end = end.finish(); + + output + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr new file mode 100644 index 00000000000..8dc17f397bd --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr @@ -0,0 +1,35 @@ +use dep::types::{ + abis::{ + accumulated_data::{public_accumulated_data_builder::PublicAccumulatedDataBuilder}, gas::Gas, + log_hash::LogHash +}, + constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE} +}; + +fn meter_gas_used(data: PublicAccumulatedDataBuilder) -> Gas { + let mut metered_bytes = 0; + metered_bytes += data.new_note_hashes.len() * DA_BYTES_PER_FIELD; + metered_bytes += data.new_nullifiers.len() * DA_BYTES_PER_FIELD; + metered_bytes += data.new_l2_to_l1_msgs.len() * DA_BYTES_PER_FIELD; + + let note_encrypted_log_preimages_length = data.note_encrypted_logs_hashes.storage.fold(0, |len, l: LogHash| len + l.length); + metered_bytes += note_encrypted_log_preimages_length as u32; + + let encrypted_log_preimages_length = data.encrypted_logs_hashes.storage.fold(0, |len, l: LogHash| len + l.length); + metered_bytes += encrypted_log_preimages_length as u32; + + let unencrypted_log_preimages_length = data.unencrypted_logs_hashes.storage.fold(0, |len, l: LogHash| len + l.length); + metered_bytes += unencrypted_log_preimages_length as u32; + + // TODO(gas): add AVM_STARTUP_L2_GAS + + Gas::new(metered_bytes * DA_GAS_PER_BYTE, 0) +} + +pub fn meter_gas_used_non_revertible(data: PublicAccumulatedDataBuilder) -> Gas { + meter_gas_used(data) + Gas::tx_overhead() +} + +pub fn meter_gas_used_revertible(data: PublicAccumulatedDataBuilder, teardown_gas: Gas) -> Gas { + meter_gas_used(data) + Gas::new(teardown_gas.da_gas, teardown_gas.l2_gas) +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/split_to_public.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/split_to_public.nr new file mode 100644 index 00000000000..0c9bb008ffa --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/split_to_public.nr @@ -0,0 +1,111 @@ +use dep::types::abis::{ + accumulated_data::{ + private_accumulated_data_builder::PrivateAccumulatedDataBuilder, + public_accumulated_data_builder::PublicAccumulatedDataBuilder +} +}; + +pub fn split_to_public( + data: PrivateAccumulatedDataBuilder, + min_revertible_side_effect_counter: u32 +) -> (PublicAccumulatedDataBuilder, PublicAccumulatedDataBuilder) { + let mut non_revertible_builder = PublicAccumulatedDataBuilder::empty(); + let mut revertible_builder = PublicAccumulatedDataBuilder::empty(); + + let note_hashes = data.new_note_hashes; + for i in 0..note_hashes.max_len() { + if i < note_hashes.len() { + let note_hash = note_hashes.get_unchecked(i); + let public_note_hash = note_hash.expose_to_public(); + if note_hash.counter() < min_revertible_side_effect_counter { + non_revertible_builder.new_note_hashes.push(public_note_hash); + } else { + revertible_builder.new_note_hashes.push(public_note_hash); + } + } + } + + let nullifiers = data.new_nullifiers; + // First nullifier is always non-revertible. + let first_nullifier = nullifiers.get_unchecked(0).expose_to_public(); + non_revertible_builder.new_nullifiers.push(first_nullifier); + for i in 1..nullifiers.max_len() { + if i < nullifiers.len() { + let nullifier = nullifiers.get_unchecked(i); + let public_nullifier = nullifier.expose_to_public(); + if nullifier.counter() < min_revertible_side_effect_counter { + non_revertible_builder.new_nullifiers.push(public_nullifier); + } else { + revertible_builder.new_nullifiers.push(public_nullifier); + } + } + } + + let l2_to_l1_msgs = data.new_l2_to_l1_msgs; + for i in 0..l2_to_l1_msgs.max_len() { + if i < l2_to_l1_msgs.len() { + let msg = l2_to_l1_msgs.get_unchecked(i); + if msg.counter() < min_revertible_side_effect_counter { + non_revertible_builder.new_l2_to_l1_msgs.push(msg.message.content); + } else { + revertible_builder.new_l2_to_l1_msgs.push(msg.message.content); + } + } + } + + let note_encrypted_logs_hashes = data.note_encrypted_logs_hashes; + for i in 0..note_encrypted_logs_hashes.max_len() { + if i < note_encrypted_logs_hashes.len() { + let note_encrypted_log_hash = note_encrypted_logs_hashes.get_unchecked(i); + let public_log_hash = note_encrypted_log_hash.expose_to_public(); + if note_encrypted_log_hash.counter < min_revertible_side_effect_counter { + non_revertible_builder.note_encrypted_logs_hashes.push(public_log_hash); + } else { + revertible_builder.note_encrypted_logs_hashes.push(public_log_hash); + } + } + } + + let encrypted_logs_hashes = data.encrypted_logs_hashes; + for i in 0..encrypted_logs_hashes.max_len() { + if i < encrypted_logs_hashes.len() { + let encrypted_log_hash = encrypted_logs_hashes.get_unchecked(i); + let public_log_hash = encrypted_log_hash.expose_to_public(); + if encrypted_log_hash.counter() < min_revertible_side_effect_counter { + non_revertible_builder.encrypted_logs_hashes.push(public_log_hash); + } else { + revertible_builder.encrypted_logs_hashes.push(public_log_hash); + } + } + } + + let unencrypted_logs_hashes = data.unencrypted_logs_hashes; + for i in 0..unencrypted_logs_hashes.max_len() { + if i < unencrypted_logs_hashes.len() { + let unencrypted_log_hash = unencrypted_logs_hashes.get_unchecked(i); + let public_log_hash = unencrypted_log_hash.expose_to_public(); + if unencrypted_log_hash.counter() < min_revertible_side_effect_counter { + non_revertible_builder.unencrypted_logs_hashes.push(public_log_hash); + } else { + revertible_builder.unencrypted_logs_hashes.push(public_log_hash); + } + } + } + + let public_call_stack = data.public_call_stack; + for i in 0..public_call_stack.max_len() { + if i < public_call_stack.len() { + let call_stack_item = public_call_stack.get_unchecked(i); + // TODO: Hide the counter of a public call request. + // let public_call_request = call_stack_item.expose_to_public(); + let public_call_request = call_stack_item; + if call_stack_item.start_side_effect_counter < min_revertible_side_effect_counter { + non_revertible_builder.public_call_stack.push(public_call_request); + } else { + revertible_builder.public_call_stack.push(public_call_request); + } + } + } + + (non_revertible_builder, revertible_builder) +} 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 new file mode 100644 index 00000000000..e642a8f4539 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr @@ -0,0 +1,194 @@ +mod tail_to_public_output_hints; + +use crate::components::{ + tail_output_validator::validate_value_transformation::{validate_transformed_values, validate_value_transformation}, + tail_to_public_output_validator::tail_to_public_output_hints::{generate_tail_to_public_output_hints, TailToPublicOutputHints} +}; +use dep::types::{ + abis::{ + call_request::CallRequest, + kernel_circuit_public_inputs::{PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, + log_hash::{LogHash, ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, nullifier::Nullifier +}, + hash::{ + silo_encrypted_log_hash, silo_l2_to_l1_message, silo_note_hash, silo_nullifier, + silo_unencrypted_log_hash +}, + traits::{Empty, is_empty_array}, + utils::arrays::{assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc} +}; + +struct TailToPublicOutputValidator { + output: PublicKernelCircuitPublicInputs, + previous_kernel: PrivateKernelCircuitPublicInputs, +} + +impl TailToPublicOutputValidator { + pub fn new( + output: PublicKernelCircuitPublicInputs, + previous_kernel: PrivateKernelCircuitPublicInputs + ) -> Self { + TailToPublicOutputValidator { output, previous_kernel } + } + + pub fn validate(self) { + let hints = generate_tail_to_public_output_hints(self.previous_kernel); + self.validate_empty_values(); + self.validate_propagated_values(); + self.validate_propagated_sorted_siloed_values(hints); + self.validate_gas_limits(); + } + + fn validate_empty_values(self) { + assert_eq(self.output.revert_code, 0, "revert_code must be empty"); + + assert( + is_empty_array(self.output.end_non_revertible.public_data_update_requests), "non-revertible public_data_update_requests must be empty" + ); + assert( + is_empty_array(self.output.end.public_data_update_requests), "revertible public_data_update_requests must be empty" + ); + } + + fn validate_propagated_values(self) { + assert_eq(self.output.constants, self.previous_kernel.constants, "mismatch constants"); + + assert_eq( + self.output.validation_requests.for_rollup, self.previous_kernel.validation_requests.for_rollup, "mismatch rollup_validation_requests" + ); + + assert_eq(self.output.fee_payer, self.previous_kernel.fee_payer, "mismatch fee_payer"); + } + + fn validate_propagated_sorted_siloed_values(self, hints: TailToPublicOutputHints) { + 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; + + // new_note_hashes + let first_nullifier = output_non_revertible.new_nullifiers[0].value; + let unsiloed_note_hashes = prev_data.new_note_hashes; + for i in 0..unsiloed_note_hashes.len() { + let siloed_note_hash = silo_note_hash(unsiloed_note_hashes[i], first_nullifier, i); + assert_eq(hints.siloed_note_hashes[i].value, siloed_note_hash, "mismatch siloed note hashes"); + assert_eq(hints.siloed_note_hashes[i].counter, 0, "cannot expose note hash counter"); + } + + assert_split_sorted_transformed_value_arrays_asc( + prev_data.new_note_hashes, + hints.siloed_note_hashes, + split_counter, + output_non_revertible.new_note_hashes, + output_revertible.new_note_hashes, + hints.sorted_note_hash_hints + ); + + // new_nullifiers + validate_value_transformation( + prev_data.new_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.new_nullifiers, + hints.siloed_nullifiers, + split_counter, + output_non_revertible.new_nullifiers, + output_revertible.new_nullifiers, + hints.sorted_nullifier_hints + ); + + // new_l2_to_l1_msgs + let tx_context = self.previous_kernel.constants.tx_context; + validate_transformed_values( + prev_data.new_l2_to_l1_msgs, + hints.siloed_l2_to_l1_msgs, + |msg| silo_l2_to_l1_message(msg, tx_context.version, tx_context.chain_id) + ); + + assert_split_sorted_transformed_value_arrays_asc( + prev_data.new_l2_to_l1_msgs, + hints.siloed_l2_to_l1_msgs, + split_counter, + output_non_revertible.new_l2_to_l1_msgs, + output_revertible.new_l2_to_l1_msgs, + hints.sorted_l2_to_l1_msg_hints + ); + + // note_encrypted_logs_hashes + validate_value_transformation( + 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 + ); + + // encrypted_logs_hashes + validate_value_transformation( + prev_data.encrypted_logs_hashes, + hints.siloed_encrypted_logs_hashes, + |slh: ScopedEncryptedLogHash, lh: LogHash| (lh.value == silo_encrypted_log_hash(slh)) & (lh.length == slh.log_hash.length) & (lh.counter == 0) + ); + + assert_split_sorted_transformed_value_arrays_asc( + prev_data.encrypted_logs_hashes, + hints.siloed_encrypted_logs_hashes, + split_counter, + output_non_revertible.encrypted_logs_hashes, + output_revertible.encrypted_logs_hashes, + hints.sorted_encrypted_log_hash_hints + ); + + // unencrypted_logs_hashes + validate_value_transformation( + prev_data.unencrypted_logs_hashes, + hints.siloed_unencrypted_logs_hashes, + |slh: ScopedLogHash, lh: LogHash| (lh.value == silo_unencrypted_log_hash(slh)) & (lh.length == slh.log_hash.length) & (lh.counter == 0) + ); + + assert_split_sorted_transformed_value_arrays_asc( + prev_data.unencrypted_logs_hashes, + hints.siloed_unencrypted_logs_hashes, + split_counter, + output_non_revertible.unencrypted_logs_hashes, + output_revertible.unencrypted_logs_hashes, + hints.sorted_unencrypted_log_hash_hints + ); + + // public_call_stack + validate_value_transformation( + prev_data.public_call_stack, + hints.public_call_requests, + |cr: CallRequest, public_cr: CallRequest| (public_cr.hash == cr.hash) + & (public_cr.caller_contract_address == cr.caller_contract_address) + & (public_cr.caller_context == cr.caller_context) + // TODO: Hide the counter of a public call request. + // & (public_cr.start_side_effect_counter == 0) + & (public_cr.end_side_effect_counter == 0) + ); + + assert_split_sorted_transformed_value_arrays_desc( + prev_data.public_call_stack, + hints.public_call_requests, + split_counter, + output_non_revertible.public_call_stack, + output_revertible.public_call_stack, + hints.sorted_public_call_request_hints + ) + } + + fn validate_gas_limits(self) { + let limits = self.previous_kernel.constants.tx_context.gas_settings.gas_limits; + let total_gas_used = self.output.end_non_revertible.gas_used + self.output.end.gas_used; + assert(total_gas_used.within(limits), "The gas used exceeds the gas limits"); + } +} 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 new file mode 100644 index 00000000000..7c47b24720d --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator/tail_to_public_output_hints.nr @@ -0,0 +1,124 @@ +use dep::types::{ + abis::{ + call_request::CallRequest, kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + log_hash::{LogHash, NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash}, note_hash::NoteHash, + nullifier::Nullifier +}, + constants::{ + MAX_ENCRYPTED_LOGS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX +}, + hash::{ + silo_encrypted_log_hash, silo_l2_to_l1_message, silo_note_hash, silo_nullifier, + silo_unencrypted_log_hash +}, + messaging::l2_to_l1_message::ScopedL2ToL1Message, + utils::arrays::{sort_get_split_order_hints_asc, sort_get_split_order_hints_desc, SplitOrderHints} +}; + +struct TailToPublicOutputHints { + // Note hashes. + siloed_note_hashes: [NoteHash; MAX_NEW_NOTE_HASHES_PER_TX], + sorted_note_hash_hints: SplitOrderHints, + // Nullifiers. + siloed_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_TX], + sorted_nullifier_hints: SplitOrderHints, + // L2 to l1 msgs. + siloed_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], + 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. + siloed_encrypted_logs_hashes: [LogHash; MAX_ENCRYPTED_LOGS_PER_TX], + sorted_encrypted_log_hash_hints: SplitOrderHints, + // Unencrypted log hashes. + siloed_unencrypted_logs_hashes: [LogHash; MAX_UNENCRYPTED_LOGS_PER_TX], + sorted_unencrypted_log_hash_hints: SplitOrderHints, + // Public call requests. + public_call_requests: [CallRequest; 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.new_note_hashes; + + let first_nullifier = previous_kernel.end.new_nullifiers[0].value(); + let mut siloed_note_hashes = [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_TX]; + for i in 0..unsiloed_note_hashes.len() { + siloed_note_hashes[i].value = silo_note_hash(unsiloed_note_hashes[i], first_nullifier, i); + } + + let sorted_note_hash_hints = sort_get_split_order_hints_asc(unsiloed_note_hashes, split_counter); + + // nullifiers + let unsiloed_nullifiers = previous_kernel.end.new_nullifiers; + + let mut siloed_nullifiers = [Nullifier::empty(); MAX_NEW_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_msgs + let unsiloed_l2_to_l1_msgs = previous_kernel.end.new_l2_to_l1_msgs; + + let tx_context = previous_kernel.constants.tx_context; + let siloed_l2_to_l1_msgs = unsiloed_l2_to_l1_msgs.map( + |m: ScopedL2ToL1Message| silo_l2_to_l1_message( + m, + tx_context.version, + tx_context.chain_id, + ) + ); + + let sorted_l2_to_l1_msg_hints = sort_get_split_order_hints_asc(unsiloed_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 mut siloed_log_hashes = previous_kernel.end.encrypted_logs_hashes; + for i in 0..siloed_log_hashes.len() { + siloed_log_hashes[i].log_hash.value = silo_encrypted_log_hash(previous_kernel.end.encrypted_logs_hashes[i]); + } + let siloed_encrypted_logs_hashes = siloed_log_hashes.map(|h: ScopedEncryptedLogHash| 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 mut siloed_log_hashes = previous_kernel.end.unencrypted_logs_hashes; + for i in 0..siloed_log_hashes.len() { + siloed_log_hashes[i].log_hash.value = silo_unencrypted_log_hash(previous_kernel.end.unencrypted_logs_hashes[i]); + } + let siloed_unencrypted_logs_hashes = siloed_log_hashes.map(|h: ScopedLogHash| h.inner()); + let sorted_unencrypted_log_hash_hints = sort_get_split_order_hints_asc(previous_kernel.end.unencrypted_logs_hashes, split_counter); + + // public_call_requests + // TODO: Hide the counter of a public call request. + // let public_call_requests = previous_kernel.end.public_call_stack.map(|cr: CallRequest| cr.expose_to_public()); + let public_call_requests = previous_kernel.end.public_call_stack; + let sorted_public_call_request_hints = sort_get_split_order_hints_desc(previous_kernel.end.public_call_stack, split_counter); + + TailToPublicOutputHints { + siloed_note_hashes, + sorted_note_hash_hints, + sorted_nullifier_hints, + siloed_nullifiers, + sorted_l2_to_l1_msg_hints, + siloed_l2_to_l1_msgs, + note_encrypted_logs_hashes, + sorted_note_encrypted_log_hash_hints, + siloed_encrypted_logs_hashes, + sorted_encrypted_log_hash_hints, + siloed_unencrypted_logs_hashes, + sorted_unencrypted_log_hash_hints, + public_call_requests, + sorted_public_call_request_hints + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr index 0ea46141d8d..738e05833cb 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr @@ -14,9 +14,8 @@ use dep::types::{ }; struct PrivateKernelInitHints { + // TODO: Remove note_hash_nullifier_counters. note_hash_nullifier_counters: [u32; MAX_NEW_NOTE_HASHES_PER_CALL], - // TODO: Build the following hint in noir. - first_revertible_private_call_request_index: u32, } // Initialization struct for private inputs to the private kernel @@ -29,7 +28,7 @@ struct PrivateKernelInitCircuitPrivateInputs { impl PrivateKernelInitCircuitPrivateInputs { unconstrained fn generate_output(self) -> PrivateKernelCircuitPublicInputs { let private_call_public_inputs = self.private_call.call_stack_item.public_inputs; - PrivateKernelCircuitPublicInputsComposer::new_from_tx_request(self.tx_request, private_call_public_inputs).compose( + PrivateKernelCircuitPublicInputsComposer::new_from_tx_request(self.tx_request, private_call_public_inputs).with_private_call( private_call_public_inputs, self.private_call.call_stack_item.contract_address, self.hints.note_hash_nullifier_counters, @@ -44,7 +43,7 @@ impl PrivateKernelInitCircuitPrivateInputs { // Validate inputs. let private_call_data_validator = PrivateCallDataValidator::new(self.private_call); - private_call_data_validator.validate_as_first_call(self.hints.first_revertible_private_call_request_index); + private_call_data_validator.validate_as_first_call(); private_call_data_validator.validate_against_tx_request(self.tx_request); private_call_data_validator.validate(output.end.new_note_hashes); if !std::runtime::is_unconstrained() { @@ -87,10 +86,7 @@ mod tests { pub fn new() -> Self { let private_call = FixtureBuilder::new(); let tx_request = private_call.build_tx_request(); - let hints = PrivateKernelInitHints { - note_hash_nullifier_counters: [0; MAX_NEW_NOTE_HASHES_PER_CALL], - first_revertible_private_call_request_index: 0 - }; + let hints = PrivateKernelInitHints { note_hash_nullifier_counters: [0; MAX_NEW_NOTE_HASHES_PER_CALL] }; PrivateKernelInitInputsBuilder { tx_request, private_call, hints } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index c2c0adceda9..c46cec8a4b1 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -25,7 +25,7 @@ struct PrivateKernelInnerCircuitPrivateInputs { impl PrivateKernelInnerCircuitPrivateInputs { unconstrained fn generate_output(self) -> PrivateKernelCircuitPublicInputs { - PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(self.previous_kernel.public_inputs).compose( + PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(self.previous_kernel.public_inputs).pop_top_call_request().with_private_call( self.private_call.call_stack_item.public_inputs, self.private_call.call_stack_item.contract_address, self.hints.note_hash_nullifier_counters, 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 65ef73ebaa1..149b0187d94 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 @@ -1,56 +1,16 @@ use crate::components::{ - kernel_circuit_public_inputs_composer::KernelCircuitPublicInputsComposer, - kernel_circuit_output_hints::generate_hints, - kernel_circuit_output_validator::KernelCircuitOutputValidator, - previous_kernel_validator::PreviousKernelValidator + previous_kernel_validator::PreviousKernelValidator, tail_output_composer::TailOutputComposer, + tail_output_validator::TailOutputValidator }; -use dep::types::{ - abis::{ - private_kernel_data::PrivateKernelData, kernel_circuit_public_inputs::KernelCircuitPublicInputs, - note_hash::ScopedNoteHash, nullifier::ScopedNullifier, - log_hash::{NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash} -}, - constants::{ - MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX -} -}; - -// TODO: Build hints in noir. -struct PrivateKernelTailHints { - sorted_new_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_new_note_hashes_indexes: [u32; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_new_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], - sorted_new_nullifiers_indexes: [u32; MAX_NEW_NULLIFIERS_PER_TX], - sorted_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hashes_indexes: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes_indexes: [u32; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes_indexes: [u32; MAX_UNENCRYPTED_LOGS_PER_TX], -} +use dep::types::{abis::{private_kernel_data::PrivateKernelData, kernel_circuit_public_inputs::KernelCircuitPublicInputs}}; struct PrivateKernelTailCircuitPrivateInputs { previous_kernel: PrivateKernelData, - hints: PrivateKernelTailHints, } impl PrivateKernelTailCircuitPrivateInputs { unconstrained fn generate_output(self) -> KernelCircuitPublicInputs { - KernelCircuitPublicInputsComposer::new( - self.previous_kernel, - self.hints.sorted_new_note_hashes, - self.hints.sorted_new_note_hashes_indexes, - self.hints.sorted_new_nullifiers, - self.hints.sorted_new_nullifiers_indexes, - self.hints.sorted_note_encrypted_log_hashes, - self.hints.sorted_note_encrypted_log_hashes_indexes, - self.hints.sorted_encrypted_log_hashes, - self.hints.sorted_encrypted_log_hashes_indexes, - self.hints.sorted_unencrypted_log_hashes, - self.hints.sorted_unencrypted_log_hashes_indexes - ).compose().finish() + TailOutputComposer::new(self.previous_kernel.public_inputs).finish() } pub fn execute(self) -> KernelCircuitPublicInputs { @@ -66,43 +26,31 @@ impl PrivateKernelTailCircuitPrivateInputs { // Validate output. if !std::runtime::is_unconstrained() { - let hints = generate_hints(self.previous_kernel.public_inputs); - KernelCircuitOutputValidator::new(output, self.previous_kernel.public_inputs).validate(hints); + TailOutputValidator::new(output, self.previous_kernel.public_inputs).validate(); } + output } } mod tests { - use crate::private_kernel_tail::{PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailHints}; - use dep::reset_kernel_lib::{ - tests::{ - note_hash_read_request_hints_builder::NoteHashReadRequestHintsBuilder, - nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, - squash_transient_data::{squash_transient_note_hashes, squash_transient_nullifiers, squash_transient_logs} - }, - reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} - }; + use crate::private_kernel_tail::PrivateKernelTailCircuitPrivateInputs; use dep::types::constants::{ - MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, - MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, - DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, GENERATOR_INDEX__IVSK_M + MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, + GENERATOR_INDEX__IVSK_M }; use dep::types::{ abis::{ kernel_circuit_public_inputs::KernelCircuitPublicInputs, max_block_number::MaxBlockNumber, - note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, - log_hash::{NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash}, gas::Gas + note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, gas::Gas }, address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, hash::{ sha256_to_field, silo_note_hash, silo_nullifier, compute_siloed_encrypted_log_hash, compute_siloed_unencrypted_log_hash }, - tests::{fixture_builder::FixtureBuilder, sort::sort_get_sorted_hints}, - utils::{arrays::array_length}, traits::{Empty, is_empty, is_empty_array}, - grumpkin_point::GrumpkinPoint + tests::fixture_builder::FixtureBuilder, utils::{arrays::array_length}, + traits::{Empty, is_empty}, grumpkin_point::GrumpkinPoint }; // TODO: Reduce the duplicated code/tests for PrivateKernelTailInputs and PrivateKernelTailToPublicInputs. @@ -114,8 +62,7 @@ mod tests { pub fn new() -> Self { let mut previous_kernel = FixtureBuilder::new(); previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1_000_000, 1_000_000); - - previous_kernel.append_new_nullifiers(1); + previous_kernel.set_first_nullifier(); PrivateKernelTailInputsBuilder { previous_kernel } } @@ -142,55 +89,7 @@ mod tests { } pub fn execute(&mut self) -> KernelCircuitPublicInputs { - let sorted = sort_get_sorted_hints( - self.previous_kernel.new_note_hashes.storage, - |a: ScopedNoteHash, b: ScopedNoteHash| a.counter() < b.counter() - ); - let sorted_new_note_hashes = sorted.sorted_array; - let sorted_new_note_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.new_nullifiers.storage, - |a: ScopedNullifier, b: ScopedNullifier| a.counter() < b.counter() - ); - let sorted_new_nullifiers = sorted.sorted_array; - let sorted_new_nullifiers_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.note_encrypted_logs_hashes.storage, - |a: NoteLogHash, b: NoteLogHash| a.counter < b.counter - ); - let sorted_note_encrypted_log_hashes = sorted.sorted_array; - let sorted_note_encrypted_log_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.encrypted_logs_hashes.storage, - |a: ScopedEncryptedLogHash, b: ScopedEncryptedLogHash| a.counter() < b.counter() - ); - let sorted_encrypted_log_hashes = sorted.sorted_array; - let sorted_encrypted_log_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.unencrypted_logs_hashes.storage, - |a: ScopedLogHash, b: ScopedLogHash| a.counter() < b.counter() - ); - let sorted_unencrypted_log_hashes = sorted.sorted_array; - let sorted_unencrypted_log_hashes_indexes = sorted.sorted_index_hints; - - let hints = PrivateKernelTailHints { - sorted_new_note_hashes, - sorted_new_note_hashes_indexes, - sorted_new_nullifiers, - sorted_new_nullifiers_indexes, - sorted_note_encrypted_log_hashes, - sorted_note_encrypted_log_hashes_indexes, - sorted_encrypted_log_hashes, - sorted_encrypted_log_hashes_indexes, - sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hashes_indexes - }; - - let kernel = PrivateKernelTailCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), hints }; + let kernel = PrivateKernelTailCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data() }; kernel.execute() } @@ -204,7 +103,7 @@ mod tests { } #[test] - unconstrained fn execution_succeeded() { + fn execution_succeeded() { let mut builder = PrivateKernelTailInputsBuilder::new(); let public_inputs = builder.execute(); @@ -212,7 +111,7 @@ mod tests { } #[test] - unconstrained fn propagate_previous_kernel_max_block_number() { + fn propagate_previous_kernel_max_block_number() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.max_block_number = MaxBlockNumber::new(13); let public_inputs = builder.execute(); @@ -221,7 +120,7 @@ mod tests { } #[test] - unconstrained fn logs_are_handled_as_expected() { + fn logs_are_handled_as_expected() { let mut builder = PrivateKernelTailInputsBuilder::new(); // Logs for the previous call stack. let prev_encrypted_logs_hash = 80; @@ -278,8 +177,15 @@ mod tests { assert_eq(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); } + unconstrained fn compute_hash_bytes( + siloed_encrypted_logs_hash: Field, + new_siloed_encrypted_logs_hash: Field + ) -> [u8; MAX_ENCRYPTED_LOGS_PER_TX * 32] { + siloed_encrypted_logs_hash.to_be_bytes(32).append(new_siloed_encrypted_logs_hash.to_be_bytes(32)).append(&[0; MAX_ENCRYPTED_LOGS_PER_TX * 32 - 64]).as_array() + } + #[test] - unconstrained fn encrypted_logs_are_hashed_as_expected() { + fn encrypted_logs_are_hashed_as_expected() { let mut builder = PrivateKernelTailInputsBuilder::new(); // Logs for the previous call stack. let prev_encrypted_logs_hash = 80; @@ -313,18 +219,14 @@ mod tests { builder.previous_kernel.encrypted_logs_hashes.storage[1].log_hash.randomness, new_encrypted_logs_hash ); - // noir-fmt:ignore - let hash_bytes: [u8; MAX_ENCRYPTED_LOGS_PER_TX * 32] = siloed_encrypted_logs_hash - .to_be_bytes(32) - .append(new_siloed_encrypted_logs_hash.to_be_bytes(32)) - .append(&[0; MAX_ENCRYPTED_LOGS_PER_TX * 32 - 64]) - .as_array(); + + let hash_bytes = compute_hash_bytes(siloed_encrypted_logs_hash, new_siloed_encrypted_logs_hash); let expected_encrypted_logs_hash = sha256_to_field(hash_bytes); assert_eq(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); } #[test] - unconstrained fn ordering_of_note_hashes_and_nullifiers() { + fn ordering_of_note_hashes_and_nullifiers() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(10); @@ -355,7 +257,7 @@ mod tests { } #[test] - unconstrained fn native_empty_nullified_note_hash_means_persistent_nullifier_0() { + fn native_empty_nullified_note_hash_means_persistent_nullifier_0() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(2); builder.previous_kernel.append_new_nullifiers(2); @@ -367,28 +269,28 @@ mod tests { } #[test(should_fail_with="Private call stack must be empty when executing the tail circuit")] - unconstrained fn non_empty_private_call_stack_should_fail() { + fn non_empty_private_call_stack_should_fail() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.add_private_call_request(1, false); builder.failed(); } #[test(should_fail_with="Public call stack must be empty when executing the tail circuit")] - unconstrained fn non_empty_public_call_stack_should_fail() { + fn non_empty_public_call_stack_should_fail() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.push_public_call_request(1, false); builder.failed(); } #[test(should_fail_with="Public teardown call request must be empty when executing the tail circuit")] - unconstrained fn non_empty_public_teardown_call_request_should_fail() { + fn non_empty_public_teardown_call_request_should_fail() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.push_public_teardown_call_request(1, false); builder.failed(); } #[test(should_fail_with="Non empty note hash read requests")] - unconstrained fn non_empty_note_hash_read_requests() { + fn non_empty_note_hash_read_requests() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(3); let _void = builder.previous_kernel.add_read_request_for_pending_note_hash(1); @@ -396,7 +298,7 @@ mod tests { } #[test(should_fail_with="Non empty nullifier read requests")] - unconstrained fn non_empty_nullifier_read_requests() { + fn non_empty_nullifier_read_requests() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_new_nullifiers(3); let _void = builder.previous_kernel.add_read_request_for_pending_nullifier(1); @@ -404,14 +306,14 @@ mod tests { } #[test(should_fail_with="Non empty key validation requests")] - unconstrained fn non_empty_key_validations() { + fn non_empty_key_validations() { let mut builder = PrivateKernelTailInputsBuilder::new(); let _void = builder.previous_kernel.add_request_for_key_validation(GrumpkinPoint::new(1, 2), 27, GENERATOR_INDEX__IVSK_M); builder.failed(); } #[test] - unconstrained fn empty_tx_consumes_teardown_limits_plus_fixed_gas() { + fn empty_tx_consumes_teardown_limits_plus_fixed_gas() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300); let public_inputs = builder.execute(); @@ -427,7 +329,7 @@ mod tests { } #[test(should_fail_with="The gas used exceeds the gas limits")] - unconstrained fn gas_limits_are_enforced() { + fn gas_limits_are_enforced() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300); builder.previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1, 1); @@ -435,7 +337,7 @@ mod tests { } #[test] - unconstrained fn propagate_fee_payer() { + fn propagate_fee_payer() { // Check that we carry forward if the fee payer is already set let mut builder = PrivateKernelTailInputsBuilder::new(); let fee_payer = AztecAddress::from_field(123); 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 c653a71dd07..0b423767d07 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 @@ -1,43 +1,23 @@ use crate::components::{ - kernel_circuit_public_inputs_composer::KernelCircuitPublicInputsComposer, - previous_kernel_validator::PreviousKernelValidator + previous_kernel_validator::PreviousKernelValidator, + tail_to_public_output_composer::TailToPublicOutputComposer, + tail_to_public_output_validator::TailToPublicOutputValidator }; -use dep::types::{ - abis::{ - private_kernel_data::PrivateKernelData, - kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, note_hash::ScopedNoteHash, - nullifier::ScopedNullifier, log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, - call_request::CallRequest -}, - constants::{ - MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX -} -}; - -struct PrivateKernelTailToPublicHints { - sorted_new_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_new_note_hashes_indexes: [u32; MAX_NEW_NOTE_HASHES_PER_TX], - sorted_new_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], - sorted_new_nullifiers_indexes: [u32; MAX_NEW_NULLIFIERS_PER_TX], - sorted_note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_note_encrypted_log_hashes_indexes: [u32; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes: [ScopedEncryptedLogHash; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_encrypted_log_hashes_indexes: [u32; MAX_ENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX], - sorted_unencrypted_log_hashes_indexes: [u32; MAX_UNENCRYPTED_LOGS_PER_TX], - sorted_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], - sorted_call_requests_indexes: [u32; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], -} +use dep::types::{abis::{private_kernel_data::PrivateKernelData, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs}}; struct PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: PrivateKernelData, - hints: PrivateKernelTailToPublicHints, } impl PrivateKernelTailToPublicCircuitPrivateInputs { + unconstrained fn generate_output(self) -> PublicKernelCircuitPublicInputs { + TailToPublicOutputComposer::new(self.previous_kernel.public_inputs).finish() + } + pub fn execute(self) -> PublicKernelCircuitPublicInputs { + // Generate output. + let output = self.generate_output(); + // Validate inputs. PreviousKernelValidator::new(self.previous_kernel.public_inputs).validate_for_private_tail_to_public(); if !std::runtime::is_unconstrained() { @@ -45,50 +25,26 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { self.previous_kernel.verify(); } - KernelCircuitPublicInputsComposer::new( - self.previous_kernel, - self.hints.sorted_new_note_hashes, - self.hints.sorted_new_note_hashes_indexes, - self.hints.sorted_new_nullifiers, - self.hints.sorted_new_nullifiers_indexes, - self.hints.sorted_note_encrypted_log_hashes, - self.hints.sorted_note_encrypted_log_hashes_indexes, - self.hints.sorted_encrypted_log_hashes, - self.hints.sorted_encrypted_log_hashes_indexes, - self.hints.sorted_unencrypted_log_hashes, - self.hints.sorted_unencrypted_log_hashes_indexes - ).compose_public( - self.hints.sorted_call_requests, - self.hints.sorted_call_requests_indexes - ).finish_to_public() + // Validate output. + if !dep::std::runtime::is_unconstrained() { + TailToPublicOutputValidator::new(output, self.previous_kernel.public_inputs).validate(); + } + + output } } mod tests { - use crate::private_kernel_tail_to_public::{PrivateKernelTailToPublicCircuitPrivateInputs, PrivateKernelTailToPublicHints}; - use dep::reset_kernel_lib::{ - tests::{ - note_hash_read_request_hints_builder::NoteHashReadRequestHintsBuilder, - nullifier_read_request_hints_builder::NullifierReadRequestHintsBuilder, - squash_transient_data::{squash_transient_note_hashes, squash_transient_nullifiers, squash_transient_logs} - }, - reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus} - }; - use dep::types::constants::{ - MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, DA_BYTES_PER_FIELD, - DA_GAS_PER_BYTE, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, GENERATOR_INDEX__TSK_M - }; + use crate::private_kernel_tail_to_public::PrivateKernelTailToPublicCircuitPrivateInputs; + use dep::types::constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, GENERATOR_INDEX__TSK_M}; use dep::types::{ abis::{ - call_request::CallRequest, side_effect::Ordered, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, gas::Gas, note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, - log_hash::{LogHash, ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash} + log_hash::{LogHash, NoteLogHash} }, address::AztecAddress, hash::{silo_note_hash, silo_nullifier}, - tests::{fixture_builder::FixtureBuilder, sort::sort_get_sorted_hints}, - utils::{arrays::{array_eq, array_length}}, traits::is_empty_array, grumpkin_point::GrumpkinPoint + tests::fixture_builder::FixtureBuilder, utils::{arrays::array_eq}, grumpkin_point::GrumpkinPoint }; // TODO: Reduce the duplicated code/tests for PrivateKernelTailToPublicInputs and PrivateKernelTailInputs. @@ -100,7 +56,7 @@ mod tests { pub fn new() -> Self { let mut previous_kernel = FixtureBuilder::new(); previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1_000_000, 1_000_000); - previous_kernel.append_new_nullifiers(1); + previous_kernel.set_first_nullifier(); previous_kernel.push_public_call_request(1, false); PrivateKernelTailToPublicInputsBuilder { previous_kernel } @@ -141,64 +97,7 @@ mod tests { } pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { - let sorted = sort_get_sorted_hints( - self.previous_kernel.new_note_hashes.storage, - |a: ScopedNoteHash, b: ScopedNoteHash| a.counter() < b.counter() - ); - let sorted_new_note_hashes = sorted.sorted_array; - let sorted_new_note_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.new_nullifiers.storage, - |a: ScopedNullifier, b: ScopedNullifier| a.counter() < b.counter() - ); - let sorted_new_nullifiers = sorted.sorted_array; - let sorted_new_nullifiers_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.note_encrypted_logs_hashes.storage, - |a: NoteLogHash, b: NoteLogHash| a.counter < b.counter - ); - let sorted_note_encrypted_log_hashes = sorted.sorted_array; - let sorted_note_encrypted_log_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.encrypted_logs_hashes.storage, - |a: ScopedEncryptedLogHash, b: ScopedEncryptedLogHash| a.counter() < b.counter() - ); - let sorted_encrypted_log_hashes = sorted.sorted_array; - let sorted_encrypted_log_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.unencrypted_logs_hashes.storage, - |a: ScopedLogHash, b: ScopedLogHash| a.counter() < b.counter() - ); - let sorted_unencrypted_log_hashes = sorted.sorted_array; - let sorted_unencrypted_log_hashes_indexes = sorted.sorted_index_hints; - - let sorted = sort_get_sorted_hints( - self.previous_kernel.public_call_requests.storage, - |a: CallRequest, b: CallRequest| a.counter() > b.counter() - ); - let sorted_call_requests = sorted.sorted_array; - let sorted_call_requests_indexes = sorted.sorted_index_hints; - - let hints = PrivateKernelTailToPublicHints { - sorted_new_note_hashes, - sorted_new_note_hashes_indexes, - sorted_new_nullifiers, - sorted_new_nullifiers_indexes, - sorted_note_encrypted_log_hashes, - sorted_note_encrypted_log_hashes_indexes, - sorted_encrypted_log_hashes, - sorted_encrypted_log_hashes_indexes, - sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hashes_indexes, - sorted_call_requests, - sorted_call_requests_indexes - }; - - let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), hints }; + let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data() }; kernel.execute() } @@ -212,7 +111,7 @@ mod tests { } #[test] - unconstrained fn ordering_of_note_hashes_and_nullifiers() { + fn ordering_of_note_hashes_and_nullifiers() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(10); @@ -234,30 +133,32 @@ mod tests { let public_inputs = builder.execute(); + let first_nullifier = builder.previous_kernel.new_nullifiers.get(0); + assert_eq(public_inputs.end_non_revertible.new_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.new_note_hashes[i].eq(output_note_hashes[i])); - assert(public_inputs.end.new_nullifiers[i].eq(output_nullifiers[i])); + assert(public_inputs.end.new_nullifiers[i].eq(output_nullifiers[i + 1])); } } #[test(should_fail_with="Private call stack must be empty when executing the tail circuit")] - unconstrained fn non_empty_private_call_stack_should_fail() { + fn non_empty_private_call_stack_should_fail() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.add_private_call_request(1, false); builder.failed(); } #[test(should_fail_with="Must have public calls when exporting public kernel data from the tail circuit")] - unconstrained fn no_public_calls_should_fail() { + fn no_public_calls_should_fail() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.public_call_requests = BoundedVec::new(); builder.failed(); } #[test] - unconstrained fn can_run_with_only_teardown() { + fn can_run_with_only_teardown() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.public_call_requests = BoundedVec::new(); builder.previous_kernel.push_public_teardown_call_request(1, false); @@ -266,7 +167,7 @@ mod tests { } #[test] - unconstrained fn split_nullifiers_into_non_revertible() { + 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_new_nullifiers(2); @@ -301,7 +202,7 @@ mod tests { } #[test] - unconstrained fn split_note_hashes_into_non_revertible() { + fn split_note_hashes_into_non_revertible() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); // expect 2 non-revertible note hashes @@ -365,7 +266,7 @@ mod tests { } #[test(should_fail_with="Non empty note hash read requests")] - unconstrained fn non_empty_note_hash_read_requests() { + fn non_empty_note_hash_read_requests() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.append_new_note_hashes(3); let _void = builder.previous_kernel.add_read_request_for_pending_note_hash(1); @@ -373,7 +274,7 @@ mod tests { } #[test(should_fail_with="Non empty nullifier read requests")] - unconstrained fn non_empty_nullifier_read_requests() { + fn non_empty_nullifier_read_requests() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.append_new_nullifiers(3); let _void = builder.previous_kernel.add_read_request_for_pending_nullifier(1); @@ -381,24 +282,27 @@ mod tests { } #[test(should_fail_with="Non empty key validation requests")] - unconstrained fn non_empty_key_validations() { + fn non_empty_key_validations() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); let _void = builder.previous_kernel.add_request_for_key_validation(GrumpkinPoint::new(1, 2), 27, GENERATOR_INDEX__TSK_M); builder.failed(); } #[test] - unconstrained fn empty_tx_consumes_teardown_limits_plus_fixed_gas() { + fn empty_tx_consumes_teardown_limits_plus_fixed_gas() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300); let public_inputs = builder.execute(); - let expected_gas_consumed = Gas::new(300, 300) + Gas::tx_overhead(); - assert_eq(public_inputs.end.gas_used, expected_gas_consumed); + let gas_for_first_nullifier = Gas::new(DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, 0); + let expected_non_revertible_gas_consumed = Gas::tx_overhead() + gas_for_first_nullifier; + assert_eq(public_inputs.end_non_revertible.gas_used, expected_non_revertible_gas_consumed); + let expected_revertible_gas_consumed = Gas::new(300, 300); + assert_eq(public_inputs.end.gas_used, expected_revertible_gas_consumed); } #[test(should_fail_with="The gas used exceeds the gas limits")] - unconstrained fn gas_limits_are_enforced() { + fn gas_limits_are_enforced() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300); builder.previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1, 1); @@ -406,7 +310,7 @@ mod tests { } #[test] - unconstrained fn propagate_fee_payer() { + fn propagate_fee_payer() { // Check that we carry forward if the fee payer is already set let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); let fee_payer = AztecAddress::from_field(123); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests.nr index 1ee505ce419..c400947751f 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests.nr @@ -1,4 +1,8 @@ -mod kernel_circuit_output_validator_builder; +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 tail_output_composer_builder; +mod tail_output_validator_builder; +mod tail_to_public_output_composer_builder; +mod tail_to_public_output_validator_builder; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder.nr deleted file mode 100644 index 9b95f4ad859..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder.nr +++ /dev/null @@ -1,34 +0,0 @@ -mod utils; -mod validate_accumulated_values; -mod validate_empty_values; -mod validate_propagated_sorted_siloed_values; -mod validate_propagated_values; - -use crate::components::{ - kernel_circuit_output_hints::generate_hints, - kernel_circuit_output_validator::KernelCircuitOutputValidator -}; -use dep::types::{abis::nullifier::Nullifier, address::AztecAddress, tests::fixture_builder::FixtureBuilder}; - -struct KernelCircuitOutputValidatorBuilder { - output: FixtureBuilder, - previous_kernel: FixtureBuilder -} - -impl KernelCircuitOutputValidatorBuilder { - pub fn new() -> Self { - let mut output = FixtureBuilder::new(); - let mut previous_kernel = FixtureBuilder::new(); - let first_nullifier = Nullifier { value: 123451234512345, counter: 0, note_hash: 0 }.scope(AztecAddress::zero()); - output.new_nullifiers.push(first_nullifier); - previous_kernel.new_nullifiers.push(first_nullifier); - KernelCircuitOutputValidatorBuilder { output, previous_kernel } - } - - pub fn validate(self) { - let output = self.output.to_kernel_circuit_public_inputs(); - let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - let hints = generate_hints(previous_kernel); - KernelCircuitOutputValidator::new(output, previous_kernel).validate(hints); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/utils.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/utils.nr deleted file mode 100644 index f2c36f8c82a..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/utils.nr +++ /dev/null @@ -1,6 +0,0 @@ -// Swap the items so that they are not ordered by counters. -pub fn swap_items(vec: &mut BoundedVec, from_index: u64, to_index: u64) { - let tmp = vec.storage[from_index]; - vec.storage[from_index] = vec.storage[to_index]; - vec.storage[to_index] = tmp; -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder.nr new file mode 100644 index 00000000000..b5cb4c7866d --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/previous_kernel_validator_builder.nr @@ -0,0 +1,21 @@ +use crate::components::previous_kernel_validator::PreviousKernelValidator; +use dep::types::tests::fixture_builder::FixtureBuilder; + +struct PreviousKernelValidatorBuilder { + previous_kernel: FixtureBuilder +} + +impl PreviousKernelValidatorBuilder { + pub fn new() -> Self { + let mut previous_kernel = FixtureBuilder::new(); + previous_kernel.set_first_nullifier(); + PreviousKernelValidatorBuilder { previous_kernel } + } + + pub fn validate_for_private_tail(self) { + let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); + PreviousKernelValidator::new(previous_kernel).validate_for_private_tail(); + } +} + +// TODO: Add tests. \ No newline at end of file diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder.nr index 2dbeb1dbfee..2c57be0cfee 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder.nr @@ -18,7 +18,6 @@ use dep::types::{ struct PrivateCallDataValidatorBuilder { private_call: FixtureBuilder, - first_revertible_private_call_request_index: u32, previous_note_hashes: BoundedVec, } @@ -31,7 +30,7 @@ impl PrivateCallDataValidatorBuilder { pub fn new_from_counter(counter: u32) -> Self { let private_call = FixtureBuilder::new_from_counter(counter); let previous_note_hashes = BoundedVec::new(); - PrivateCallDataValidatorBuilder { private_call, first_revertible_private_call_request_index: 0, previous_note_hashes } + PrivateCallDataValidatorBuilder { private_call, previous_note_hashes } } pub fn is_delegate_call(&mut self) -> Self { @@ -53,7 +52,7 @@ impl PrivateCallDataValidatorBuilder { pub fn validate_as_first_call(self) { let private_call = self.private_call.to_private_call_data(); - PrivateCallDataValidator::new(private_call).validate_as_first_call(self.first_revertible_private_call_request_index); + PrivateCallDataValidator::new(private_call).validate_as_first_call(); } pub fn validate_against_tx_request(self, request: TxRequest) { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_as_first_call.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_as_first_call.nr index 122661c9047..cb2bdf3b886 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_as_first_call.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_as_first_call.nr @@ -8,7 +8,6 @@ impl PrivateCallDataValidatorBuilder { pub fn split_calls(&mut self, counter: u32) { self.private_call.min_revertible_side_effect_counter = counter; - self.first_revertible_private_call_request_index = self.private_call.private_call_requests.len(); } pub fn add_private_call_request(&mut self, counter_start: u32, counter_end: u32) { @@ -55,7 +54,7 @@ fn validate_as_first_call_split_private_calls_succeeds() { } #[test] -fn validate_as_first_call_split_private_empty_revertible_succeeds() { +fn validate_as_first_call_split_private_calls_empty_revertible_succeeds() { let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); builder.add_private_call_request(20, 30); @@ -66,7 +65,7 @@ fn validate_as_first_call_split_private_empty_revertible_succeeds() { } #[test] -fn validate_as_first_call_split_private_empty_non_revertible_succeeds() { +fn validate_as_first_call_split_private_calls_empty_non_revertible_succeeds() { let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); builder.split_calls(20); @@ -77,7 +76,7 @@ fn validate_as_first_call_split_private_empty_non_revertible_succeeds() { } #[test] -fn validate_as_first_call_split_private_full_non_revertible_succeeds() { +fn validate_as_first_call_split_private_calls_full_non_revertible_succeeds() { let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); builder.private_call.append_private_call_requests(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL); @@ -124,19 +123,6 @@ fn validate_as_first_call_split_private_calls_equal_last_non_revertible_fails() builder.validate_as_first_call(); } -#[test(should_fail_with="min_revertible_side_effect_counter must be less than or equal to the start counter of the first revertible item")] -fn validate_as_first_call_split_private_calls_greater_than_first_revertible_fails() { - let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); - - builder.add_private_call_request(20, 30); - builder.add_private_call_request(40, 50); - // Tweak the counter to be greater than the start counter of the first revertible call. - builder.split_calls(61); - builder.add_private_call_request(60, 70); - - builder.validate_as_first_call(); -} - #[test] fn validate_as_first_call_split_private_calls_0_succeeds() { let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); @@ -148,29 +134,3 @@ fn validate_as_first_call_split_private_calls_0_succeeds() { builder.validate_as_first_call(); } - -#[test(should_fail_with="min_revertible_side_effect_counter must be greater than the end counter of the last non revertible item")] -fn validate_as_first_call_split_private_calls_0_wrong_hint_fails() { - let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); - - builder.split_calls(0); - // Set the index hint to be 1. - builder.first_revertible_private_call_request_index = 1; - builder.add_private_call_request(20, 30); - builder.add_private_call_request(40, 50); - - builder.validate_as_first_call(); -} - -#[test(should_fail_with="min_revertible_side_effect_counter must be less than or equal to the start counter of the first revertible item")] -fn validate_as_first_call_split_private_calls_index_hint_greater_than_len_fails() { - let mut builder = PrivateCallDataValidatorBuilder::new_first_call(); - - builder.add_private_call_request(20, 30); - builder.add_private_call_request(40, 50); - builder.split_calls(51); - // Increase the index by 1. - builder.first_revertible_private_call_request_index += 1; - - builder.validate_as_first_call(); -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder.nr index 3720c01ce06..6a41dba5af6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder.nr @@ -38,17 +38,13 @@ impl PrivateKernelCircuitPublicInputsComposerBuilder { } pub fn new_from_previous_kernel(self) -> PrivateKernelCircuitPublicInputsComposer { - let mut previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); - // Append one private call request for the current call. - let num_private_call_requests = self.previous_kernel.private_call_requests.len(); - previous_kernel.end.private_call_stack[num_private_call_requests] = ScopedPrivateCallRequest::empty(); - previous_kernel.end.private_call_stack[num_private_call_requests].call_request.hash = 98765432; + let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(previous_kernel) } pub fn compose_from_tx_request(self) -> PrivateKernelCircuitPublicInputs { let private_call = self.private_call.to_private_call_data(); - self.new_from_tx_request().compose( + self.new_from_tx_request().with_private_call( private_call.call_stack_item.public_inputs, private_call.call_stack_item.contract_address, self.note_hash_nullifier_counters, @@ -58,8 +54,15 @@ impl PrivateKernelCircuitPublicInputsComposerBuilder { } pub fn compose_from_previous_kernel(self) -> PrivateKernelCircuitPublicInputs { + // Append one private call request for the previous kernel. + let mut previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); + let num_private_call_requests = self.previous_kernel.private_call_requests.len(); + previous_kernel.end.private_call_stack[num_private_call_requests] = ScopedPrivateCallRequest::empty(); + previous_kernel.end.private_call_stack[num_private_call_requests].call_request.hash = 98765432; + let private_call = self.private_call.to_private_call_data(); - self.new_from_previous_kernel().compose( + + PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(previous_kernel).pop_top_call_request().with_private_call( private_call.call_stack_item.public_inputs, private_call.call_stack_item.contract_address, self.note_hash_nullifier_counters, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder.nr new file mode 100644 index 00000000000..b7365128751 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_composer_builder.nr @@ -0,0 +1,28 @@ +use crate::components::tail_output_composer::TailOutputComposer; +use dep::types::{abis::kernel_circuit_public_inputs::KernelCircuitPublicInputs, tests::fixture_builder::FixtureBuilder}; + +struct TailOutputComposerBuilder { + previous_kernel: FixtureBuilder, +} + +impl TailOutputComposerBuilder { + pub fn new() -> Self { + let mut previous_kernel = FixtureBuilder::new(); + previous_kernel.set_first_nullifier(); // Need the first nullifier to silo note hashes. + 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); + composer.finish() + } +} + +// TODO: Add tests. \ No newline at end of file diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder.nr new file mode 100644 index 00000000000..6baadc391fb --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder.nr @@ -0,0 +1,28 @@ +mod validate_accumulated_values; +mod validate_empty_values; +mod validate_propagated_sorted_siloed_values; +mod validate_propagated_values; + +use crate::components::tail_output_validator::TailOutputValidator; +use dep::types::tests::fixture_builder::FixtureBuilder; + +struct TailOutputValidatorBuilder { + output: FixtureBuilder, + previous_kernel: FixtureBuilder +} + +impl TailOutputValidatorBuilder { + pub fn new() -> Self { + let mut output = FixtureBuilder::new(); + let mut previous_kernel = FixtureBuilder::new(); + output.set_first_nullifier(); + previous_kernel.set_first_nullifier(); + TailOutputValidatorBuilder { output, previous_kernel } + } + + pub fn validate(self) { + let output = self.output.to_kernel_circuit_public_inputs(); + let previous_kernel = self.previous_kernel.to_private_kernel_circuit_public_inputs(); + TailOutputValidator::new(output, previous_kernel).validate(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_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 similarity index 83% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/validate_accumulated_values.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_accumulated_values.nr index 8f50b622279..cb2dedc44ab 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_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 @@ -1,4 +1,5 @@ -use crate::tests::kernel_circuit_output_validator_builder::{KernelCircuitOutputValidatorBuilder, utils::swap_items}; +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. @@ -8,7 +9,7 @@ use crate::tests::kernel_circuit_output_validator_builder::{KernelCircuitOutputV #[test] fn validate_accumulated_values_note_encrypted_log_hashes_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_note_encrypted_log_hashes(3); builder.output.append_note_encrypted_log_hashes(3); @@ -19,7 +20,7 @@ fn validate_accumulated_values_note_encrypted_log_hashes_succeeds() { #[test] fn validate_accumulated_values_note_encrypted_log_hashes_unordered_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_note_encrypted_log_hashes(3); // Swap the items at index 0 and 2. @@ -32,7 +33,7 @@ fn validate_accumulated_values_note_encrypted_log_hashes_unordered_succeeds() { #[test(should_fail_with="mismatch note_encrypted_logs_hash")] fn validate_accumulated_values_note_encrypted_log_hashes_wrong_hash_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_note_encrypted_log_hashes(3); builder.output.append_note_encrypted_log_hashes(3); @@ -49,7 +50,7 @@ fn validate_accumulated_values_note_encrypted_log_hashes_wrong_hash_fails() { #[test] fn validate_accumulated_values_encrypted_log_hashes_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_encrypted_log_hashes(3); builder.output.append_encrypted_log_hashes(3); @@ -60,7 +61,7 @@ fn validate_accumulated_values_encrypted_log_hashes_succeeds() { #[test] fn validate_accumulated_values_encrypted_log_hashes_unordered_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_encrypted_log_hashes(3); // Swap the items at index 0 and 2. @@ -73,7 +74,7 @@ fn validate_accumulated_values_encrypted_log_hashes_unordered_succeeds() { #[test(should_fail_with="mismatch encrypted_logs_hash")] fn validate_accumulated_values_encrypted_log_hashes_wrong_hash_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_encrypted_log_hashes(3); builder.output.append_encrypted_log_hashes(3); @@ -90,7 +91,7 @@ fn validate_accumulated_values_encrypted_log_hashes_wrong_hash_fails() { #[test] fn validate_accumulated_values_unencrypted_log_hashes_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_unencrypted_log_hashes(3); builder.output.append_unencrypted_log_hashes(3); @@ -101,7 +102,7 @@ fn validate_accumulated_values_unencrypted_log_hashes_succeeds() { #[test] fn validate_accumulated_values_unencrypted_log_hashes_unordered_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_unencrypted_log_hashes(3); // Swap the items at index 0 and 2. @@ -114,7 +115,7 @@ fn validate_accumulated_values_unencrypted_log_hashes_unordered_succeeds() { #[test(should_fail_with="mismatch unencrypted_logs_hash")] fn validate_accumulated_values_unencrypted_log_hashes_wrong_hash_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_unencrypted_log_hashes(3); builder.output.append_unencrypted_log_hashes(3); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/validate_empty_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_empty_values.nr similarity index 60% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/validate_empty_values.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_empty_values.nr index d68ec1bb777..cc502022079 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/validate_empty_values.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_empty_values.nr @@ -1,14 +1,14 @@ -use crate::tests::kernel_circuit_output_validator_builder::KernelCircuitOutputValidatorBuilder; +use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; #[test] fn validate_empty_values_succeeds() { - let builder = KernelCircuitOutputValidatorBuilder::new(); + let builder = TailOutputValidatorBuilder::new(); builder.validate(); } #[test(should_fail_with="start_state must be empty")] fn validate_empty_values_non_empty_start_state_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.output.start_state.public_data_tree.root = 123; @@ -17,7 +17,7 @@ fn validate_empty_values_non_empty_start_state_fails() { #[test(should_fail_with="revert_code must be empty")] fn validate_empty_values_non_empty_revert_code_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.output.revert_code = 1; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_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 similarity index 80% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/validate_propagated_sorted_siloed_values.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_sorted_siloed_values.nr index 12d791671e1..bd9437270b2 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_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 @@ -1,4 +1,5 @@ -use crate::tests::kernel_circuit_output_validator_builder::{KernelCircuitOutputValidatorBuilder, utils::swap_items}; +use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; +use dep::types::tests::utils::swap_items; /** * new_note_hashes @@ -6,7 +7,7 @@ use crate::tests::kernel_circuit_output_validator_builder::{KernelCircuitOutputV #[test] fn validate_propagated_sorted_siloed_values_new_note_hashes_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_note_hashes(3); builder.output.append_siloed_note_hashes(3); @@ -16,7 +17,7 @@ fn validate_propagated_sorted_siloed_values_new_note_hashes_succeeds() { #[test] fn validate_propagated_sorted_siloed_values_new_note_hashes_unordered_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_note_hashes(3); swap_items(&mut builder.previous_kernel.new_note_hashes, 0, 2); @@ -31,7 +32,7 @@ fn validate_propagated_sorted_siloed_values_new_note_hashes_unordered_succeeds() #[test(should_fail_with="mismatch sorted values")] fn validate_propagated_sorted_siloed_values_new_note_hashes_mismatch_hash_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_note_hashes(2); builder.output.append_siloed_note_hashes(2); @@ -47,7 +48,7 @@ fn validate_propagated_sorted_siloed_values_new_note_hashes_mismatch_hash_fails( #[test] fn validate_propagated_sorted_siloed_values_new_nullifiers_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_nullifiers(3); builder.output.append_siloed_nullifiers(3); @@ -57,7 +58,7 @@ fn validate_propagated_sorted_siloed_values_new_nullifiers_succeeds() { #[test] fn validate_propagated_sorted_siloed_values_new_nullifiers_unordered_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_nullifiers(3); swap_items(&mut builder.previous_kernel.new_nullifiers, 0, 3); @@ -68,7 +69,7 @@ fn validate_propagated_sorted_siloed_values_new_nullifiers_unordered_succeeds() #[test(should_fail_with="mismatch sorted values")] fn validate_propagated_sorted_siloed_values_new_nullifiers_mismatch_hash_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_nullifiers(3); builder.output.append_siloed_nullifiers(3); @@ -84,7 +85,7 @@ fn validate_propagated_sorted_siloed_values_new_nullifiers_mismatch_hash_fails() #[test] fn validate_propagated_sorted_siloed_values_new_l2_to_l1_msgs_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_l2_to_l1_msgs(2); builder.output.append_siloed_l2_to_l1_msgs(2); @@ -94,7 +95,7 @@ fn validate_propagated_sorted_siloed_values_new_l2_to_l1_msgs_succeeds() { #[test] fn validate_propagated_sorted_siloed_values_new_l2_to_l1_msgs_unordered_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_l2_to_l1_msgs(2); swap_items(&mut builder.previous_kernel.new_l2_to_l1_msgs, 0, 1); @@ -105,7 +106,7 @@ fn validate_propagated_sorted_siloed_values_new_l2_to_l1_msgs_unordered_succeeds #[test(should_fail_with="mismatch sorted values")] fn validate_propagated_sorted_siloed_values_new_l2_to_l1_msgs_mismatch_hash_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.append_new_l2_to_l1_msgs(2); builder.output.append_siloed_l2_to_l1_msgs(2); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_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 similarity index 76% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_output_validator_builder/validate_propagated_values.nr rename to noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_output_validator_builder/validate_propagated_values.nr index 1217e37b223..f5bea6db72f 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/kernel_circuit_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,9 +1,9 @@ -use crate::tests::kernel_circuit_output_validator_builder::KernelCircuitOutputValidatorBuilder; +use crate::tests::tail_output_validator_builder::TailOutputValidatorBuilder; use dep::types::address::AztecAddress; #[test] fn validate_propagated_values_constants_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.historical_header.total_fees = 123; builder.output.historical_header.total_fees = 123; @@ -13,7 +13,7 @@ fn validate_propagated_values_constants_succeeds() { #[test(should_fail_with="mismatch constants")] fn validate_propagated_values_constants_mismatch_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.historical_header.total_fees = 123; // Tweak the value in the output. @@ -24,7 +24,7 @@ fn validate_propagated_values_constants_mismatch_fails() { #[test] fn validate_propagated_values_max_block_number_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.set_max_block_number(123); builder.output.set_max_block_number(123); @@ -34,7 +34,7 @@ fn validate_propagated_values_max_block_number_succeeds() { #[test(should_fail_with="mismatch rollup_validation_requests")] fn validate_propagated_values_max_block_number_mismatch_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.set_max_block_number(123); // Tweak the value in the output. @@ -45,7 +45,7 @@ fn validate_propagated_values_max_block_number_mismatch_fails() { #[test] fn validate_propagated_values_fee_payer_succeeds() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.set_fee_payer(AztecAddress::from_field(123)); builder.output.set_fee_payer(AztecAddress::from_field(123)); @@ -55,7 +55,7 @@ fn validate_propagated_values_fee_payer_succeeds() { #[test(should_fail_with="mismatch fee_payer")] fn validate_propagated_values_fee_payer_mismatch_fails() { - let mut builder = KernelCircuitOutputValidatorBuilder::new(); + let mut builder = TailOutputValidatorBuilder::new(); builder.previous_kernel.set_fee_payer(AztecAddress::from_field(123)); // Tweak the value in the output. diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder.nr new file mode 100644 index 00000000000..1d0042bf686 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder.nr @@ -0,0 +1,33 @@ +mod meter_gas_used; +mod split_to_public; +mod tail_to_public_output_composer; + +use crate::components::tail_to_public_output_composer::TailToPublicOutputComposer; +use dep::types::{ + abis::kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, + tests::fixture_builder::FixtureBuilder +}; + +struct TailToPublicOutputComposerBuilder { + previous_kernel: FixtureBuilder, +} + +impl TailToPublicOutputComposerBuilder { + pub fn new() -> Self { + let mut previous_kernel = FixtureBuilder::new(); + previous_kernel.set_first_nullifier(); // Need the first nullifier to silo note hashes. + 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); + composer.finish() + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr new file mode 100644 index 00000000000..a37c9b4bb6e --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr @@ -0,0 +1,79 @@ +use crate::components::tail_to_public_output_composer::meter_gas_used::{meter_gas_used_non_revertible, meter_gas_used_revertible}; +use dep::types::{ + abis::gas::Gas, constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE}, + tests::fixture_builder::FixtureBuilder +}; + +#[test] +fn meter_gas_used_non_revertible_empty_succeeds() { + let builder = FixtureBuilder::new(); + let data = builder.to_public_accumulated_data_builder(); + let gas = meter_gas_used_non_revertible(data); + assert_eq(gas, Gas::tx_overhead()); +} + +#[test] +fn meter_gas_used_non_revertible_everything_succeeds() { + let mut builder = FixtureBuilder::new(); + + builder.append_new_note_hashes(4); + builder.append_new_nullifiers(3); + builder.append_new_l2_to_l1_msgs(1); + builder.add_note_encrypted_log_hash(1001, 12, 0); + builder.add_note_encrypted_log_hash(1002, 8, 0); + builder.add_note_encrypted_log_hash(1003, 20, 0); + builder.add_encrypted_log_hash(2001, 2); + builder.add_encrypted_log_hash(2002, 6); + builder.add_unencrypted_log_hash(3001, 51); + builder.append_public_call_requests(2); + builder.end_setup(); + + let data = builder.to_public_accumulated_data_builder(); + let gas = meter_gas_used_non_revertible(data); + + let total_num_side_effects = 4 + 3 + 1; + let total_log_length = 12 + 8 + 20 // note_encrypted_log_hash + + 2 + 6 // encrypted_log_hash + + 51; // unencrypted_log_hash + let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; + + assert_eq(gas, Gas::new(computed_da_gas, 0) + Gas::tx_overhead()); +} + +#[test] +fn meter_gas_used_revertible_empty_succeeds() { + let builder = FixtureBuilder::new(); + let data = builder.to_public_accumulated_data_builder(); + let teardown_gas = Gas::new(42, 17); + let gas = meter_gas_used_revertible(data, teardown_gas); + assert_eq(gas, teardown_gas); +} + +#[test] +fn meter_gas_used_revertible_everything_succeeds() { + let mut builder = FixtureBuilder::new(); + + builder.append_new_note_hashes(4); + builder.append_new_nullifiers(3); + builder.append_new_l2_to_l1_msgs(1); + builder.add_note_encrypted_log_hash(1001, 12, 0); + builder.add_note_encrypted_log_hash(1002, 8, 0); + builder.add_note_encrypted_log_hash(1003, 20, 0); + builder.add_encrypted_log_hash(2001, 2); + builder.add_encrypted_log_hash(2002, 6); + builder.add_unencrypted_log_hash(3001, 51); + builder.append_public_call_requests(2); + builder.end_setup(); + + let data = builder.to_public_accumulated_data_builder(); + let teardown_gas = Gas::new(42, 17); + let gas = meter_gas_used_revertible(data, teardown_gas); + + let total_num_side_effects = 4 + 3 + 1; + let total_log_length = 12 + 8 + 20 // note_encrypted_log_hash + + 2 + 6 // encrypted_log_hash + + 51; // unencrypted_log_hash + let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; + + assert_eq(gas, Gas::new(computed_da_gas, 0) + teardown_gas); +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/split_to_public.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/split_to_public.nr new file mode 100644 index 00000000000..0f3c016b96c --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/split_to_public.nr @@ -0,0 +1,114 @@ +use crate::components::tail_to_public_output_composer::split_to_public::split_to_public; +use dep::types::tests::{fixture_builder::FixtureBuilder, utils::assert_array_eq}; + +#[test] +fn split_to_public_succeeds() { + let mut builder = FixtureBuilder::new(); + + // Non-revertibles. + builder.append_new_note_hashes(2); + builder.append_new_nullifiers(2); + builder.append_new_l2_to_l1_msgs(1); + builder.append_note_encrypted_log_hashes(3); + builder.append_encrypted_log_hashes(2); + builder.append_unencrypted_log_hashes(1); + builder.append_public_call_requests(1); + builder.end_setup(); + // Revertibles. + builder.append_new_note_hashes(3); + builder.append_new_nullifiers(1); + builder.append_new_l2_to_l1_msgs(1); + builder.append_note_encrypted_log_hashes(1); + builder.append_encrypted_log_hashes(2); + builder.append_unencrypted_log_hashes(1); + builder.append_public_call_requests(2); + + let combined_data = builder.to_exposed_public_accumulated_data(); + let (non_revertible, revertible) = split_to_public( + builder.to_private_accumulated_data_builder(), + builder.min_revertible_side_effect_counter + ); + + // new_note_hashes + let expected = combined_data.new_note_hashes; + assert_array_eq( + non_revertible.new_note_hashes.storage, + [expected[0], expected[1]] + ); + assert_array_eq( + revertible.new_note_hashes.storage, + [expected[2], expected[3], expected[4]] + ); + + // new_nullifiers + let expected = combined_data.new_nullifiers; + assert_array_eq( + non_revertible.new_nullifiers.storage, + [expected[0], expected[1]] + ); + assert_array_eq(revertible.new_nullifiers.storage, [expected[2]]); + + // new_l2_to_l1_msgs + let expected = combined_data.new_l2_to_l1_msgs; + assert_array_eq(non_revertible.new_l2_to_l1_msgs.storage, [expected[0]]); + assert_array_eq(revertible.new_l2_to_l1_msgs.storage, [expected[1]]); + + // note_encrypted_logs_hashes + let expected = combined_data.note_encrypted_logs_hashes; + assert_array_eq( + non_revertible.note_encrypted_logs_hashes.storage, + [expected[0], expected[1], expected[2]] + ); + assert_array_eq(revertible.note_encrypted_logs_hashes.storage, [expected[3]]); + + // encrypted_logs_hashes + let expected = combined_data.encrypted_logs_hashes; + assert_array_eq( + non_revertible.encrypted_logs_hashes.storage, + [expected[0], expected[1]] + ); + assert_array_eq( + revertible.encrypted_logs_hashes.storage, + [expected[2], expected[3]] + ); + + // unencrypted_logs_hashes + let expected = combined_data.unencrypted_logs_hashes; + assert_array_eq(non_revertible.unencrypted_logs_hashes.storage, [expected[0]]); + assert_array_eq(revertible.unencrypted_logs_hashes.storage, [expected[1]]); + + // public_call_stack + let expected = combined_data.public_call_stack; + assert_array_eq(non_revertible.public_call_stack.storage, [expected[0]]); + assert_array_eq( + revertible.public_call_stack.storage, + [expected[1], expected[2]] + ); +} + +#[test] +fn split_to_public_zero_counter_succeeds() { + let mut builder = FixtureBuilder::new(); + + builder.append_new_note_hashes(2); + builder.set_first_nullifier(); + + let combined_data = builder.to_exposed_public_accumulated_data(); + let (non_revertible, revertible) = split_to_public( + builder.to_private_accumulated_data_builder(), + builder.min_revertible_side_effect_counter + ); + + // new_note_hashes + let expected = combined_data.new_note_hashes; + assert_array_eq(non_revertible.new_note_hashes.storage, []); + assert_array_eq( + revertible.new_note_hashes.storage, + [expected[0], expected[1]] + ); + + // new_nullifiers + let expected = combined_data.new_nullifiers; + assert_array_eq(non_revertible.new_nullifiers.storage, [expected[0]]); + assert_array_eq(revertible.new_nullifiers.storage, []); +} 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 new file mode 100644 index 00000000000..32050cd2b81 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr @@ -0,0 +1,144 @@ +use crate::tests::tail_to_public_output_composer_builder::TailToPublicOutputComposerBuilder; +use dep::types::{ + abis::gas::Gas, constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE}, + tests::utils::{assert_array_eq, swap_items} +}; + +#[test] +fn tail_to_public_output_composer_succeeds() { + let mut (builder, siloed_data_builder) = TailToPublicOutputComposerBuilder::new().with_siloed_data_builder(); + + 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_new_note_hashes(4); + siloed_data_builder.append_siloed_note_hashes(4); + + builder.previous_kernel.append_new_nullifiers(2); + siloed_data_builder.append_siloed_nullifiers(2); + + builder.previous_kernel.append_new_l2_to_l1_msgs(1); + siloed_data_builder.append_siloed_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_siloed_encrypted_log_hash(2001, 2); + + builder.previous_kernel.add_unencrypted_log_hash(3001, 51); + siloed_data_builder.add_siloed_unencrypted_log_hash(3001, 51); + + builder.previous_kernel.append_public_call_requests(2); + + builder.previous_kernel.end_setup(); + + // Revertibles. + builder.previous_kernel.append_new_note_hashes(2); + siloed_data_builder.append_siloed_note_hashes(2); + + builder.previous_kernel.append_new_nullifiers(1); + siloed_data_builder.append_siloed_nullifiers(1); + + builder.previous_kernel.append_new_l2_to_l1_msgs(1); + siloed_data_builder.append_siloed_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_siloed_encrypted_log_hash(2002, 6); + builder.previous_kernel.add_encrypted_log_hash(2003, 24); + siloed_data_builder.add_siloed_encrypted_log_hash(2003, 24); + + builder.previous_kernel.add_unencrypted_log_hash(3002, 4); + siloed_data_builder.add_siloed_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(); + + // Shuffle ordered items. + swap_items(&mut builder.previous_kernel.new_note_hashes, 4, 0); + swap_items(&mut builder.previous_kernel.new_note_hashes, 3, 2); + swap_items(&mut builder.previous_kernel.new_nullifiers, 1, 3); + swap_items(&mut builder.previous_kernel.new_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.public_call_requests, 1, 2); + + // Output. + let output = builder.finish(); + + // new_note_hashes + let siloed = siloed_data.new_note_hashes; + assert_array_eq( + output.end_non_revertible.new_note_hashes, + [siloed[0], siloed[1], siloed[2], siloed[3]] + ); + assert_array_eq(output.end.new_note_hashes, [siloed[4], siloed[5]]); + + // new_nullifiers + let siloed = siloed_data.new_nullifiers; + let unsiloed = unsiloed_data.new_nullifiers; + assert_array_eq( + output.end_non_revertible.new_nullifiers, + [unsiloed[0], siloed[1], siloed[2]] + ); + assert_array_eq(output.end.new_nullifiers, [siloed[3]]); + + // new_l2_to_l1_msgs + let siloed = siloed_data.new_l2_to_l1_msgs; + assert_array_eq(output.end_non_revertible.new_l2_to_l1_msgs, [siloed[0]]); + assert_array_eq(output.end.new_l2_to_l1_msgs, [siloed[1]]); + + // note_encrypted_logs_hashes + let unsiloed = unsiloed_data.note_encrypted_logs_hashes; + assert_array_eq( + output.end_non_revertible.note_encrypted_logs_hashes, + [unsiloed[0], unsiloed[1]] + ); + assert_array_eq(output.end.note_encrypted_logs_hashes, [unsiloed[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]]); + + // unencrypted_logs_hashes + let siloed = siloed_data.unencrypted_logs_hashes; + assert_array_eq( + output.end_non_revertible.unencrypted_logs_hashes, + [siloed[0]] + ); + assert_array_eq(output.end.unencrypted_logs_hashes, [siloed[1]]); + + // public_call_stack + let unsiloed = unsiloed_data.public_call_stack; + assert_array_eq( + output.end_non_revertible.public_call_stack, + [unsiloed[1], unsiloed[0]] + ); + assert_array_eq( + output.end.public_call_stack, + [unsiloed[4], unsiloed[3], unsiloed[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 + let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; + assert_eq(output.end_non_revertible.gas_used, Gas::new(computed_da_gas, 0) + Gas::tx_overhead()); + + // Gas: revertible + let total_num_side_effects = 2 + 1 + 1; + let total_log_length = 20 // note_encrypted_log_hash + + 6 + 24 // encrypted_log_hash + + 4; // unencrypted_log_hash + let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; + assert_eq(output.end.gas_used, Gas::new(computed_da_gas, 0) + teardown_gas); +} 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 new file mode 100644 index 00000000000..1a1f05e1893 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_validator_builder.nr @@ -0,0 +1,27 @@ +use crate::components::tail_to_public_output_validator::TailToPublicOutputValidator; +use dep::types::tests::fixture_builder::FixtureBuilder; + +struct TailToPublicOutputValidatorBuilder { + output: FixtureBuilder, + previous_kernel: FixtureBuilder +} + +impl TailToPublicOutputValidatorBuilder { + pub fn new() -> Self { + let mut output = FixtureBuilder::new(); + let mut previous_kernel = FixtureBuilder::new(); + output.set_first_nullifier(); + previous_kernel.set_first_nullifier(); + TailToPublicOutputValidatorBuilder { output, previous_kernel } + } + + pub fn validate(self) { + // TODO: Split the data using min_revertible_side_effect_counter in FixtureBuilder. + 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(); + TailToPublicOutputValidator::new(output, previous_kernel).validate(); + } +} + +// TODO: Add tests. \ No newline at end of file diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr index 212a3e710c5..56cb614c267 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr @@ -215,12 +215,13 @@ mod tests { // Setup 2 new note hashes and logs on the previous kernel. builder.previous_kernel.append_new_note_hashes_with_logs(2); let previous = builder.previous_kernel.new_note_hashes.storage.map(|n: ScopedNoteHash| n.note_hash); + let prev_data = builder.previous_kernel.to_public_accumulated_data(); + let prev_note_logs = prev_data.note_encrypted_logs_hashes; // Setup 2 new note hashes on the current public inputs. let current = [ NoteHash { value: previous[1].value + 1, counter: 5 }, NoteHash { value: previous[1].value + 2, counter: 6 } ]; - let note_logs = builder.previous_kernel.note_encrypted_logs_hashes.storage; builder.public_call.public_inputs.new_note_hashes.extend_from_array(current); let siloed = current.map(|c: NoteHash| compute_siloed_note_hash(contract_address, c.value)); let new_note_hashes = [ @@ -233,7 +234,7 @@ mod tests { assert( array_eq( public_inputs.end.note_encrypted_logs_hashes, - [note_logs[0].expose_to_public(), note_logs[1].expose_to_public()] + [prev_note_logs[0], prev_note_logs[1]] ) ); } @@ -347,14 +348,15 @@ mod tests { prev_unencrypted_logs_hash, prev_unencrypted_log_preimages_length ); + let prev_data = builder.previous_kernel.to_public_accumulated_data(); let mut expected_unencrypted_logs = [ - builder.previous_kernel.unencrypted_logs_hashes.storage[0].log_hash, builder.public_call.public_inputs.unencrypted_logs_hashes.storage[0] + prev_data.unencrypted_logs_hashes[0], builder.public_call.public_inputs.unencrypted_logs_hashes.storage[0] ]; // silo the new log hash expected_unencrypted_logs[1].value = compute_siloed_unencrypted_log_hash(builder.public_call.contract_address, expected_unencrypted_logs[1].value); // we assume the encrypted log is already siloed from private kernels - let expected_encrypted_logs = [builder.previous_kernel.encrypted_logs_hashes.storage[0].expose_to_public()]; + let expected_encrypted_logs = [prev_data.encrypted_logs_hashes[0]]; let public_inputs = builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr index 648606d1a4c..a041fc2a56f 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr @@ -448,14 +448,15 @@ mod tests { prev_unencrypted_logs_hash, prev_unencrypted_log_preimages_length ); + let prev_data = builder.previous_kernel.to_public_accumulated_data(); let mut expected_unencrypted_logs = [ - builder.previous_kernel.unencrypted_logs_hashes.storage[0].log_hash, builder.public_call.public_inputs.unencrypted_logs_hashes.storage[0] + prev_data.unencrypted_logs_hashes[0], builder.public_call.public_inputs.unencrypted_logs_hashes.storage[0] ]; // silo the new log hash expected_unencrypted_logs[1].value = compute_siloed_unencrypted_log_hash(builder.public_call.contract_address, expected_unencrypted_logs[1].value); // we assume the encrypted log is already siloed from private kernels - let expected_encrypted_logs = [builder.previous_kernel.encrypted_logs_hashes.storage[0].expose_to_public()]; + let expected_encrypted_logs = [prev_data.encrypted_logs_hashes[0]]; let public_inputs = builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index f87434c241c..2acd153e8d6 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -117,9 +117,10 @@ mod tests { }, hash::{compute_siloed_nullifier, sha256_to_field}, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, - tests::{fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree, sort::sort_get_sorted_hints}, + tests::{fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree}, traits::is_empty, partial_state_reference::PartialStateReference, - utils::arrays::{array_length, array_merge}, merkle_tree::MembershipWitness + utils::arrays::{array_length, array_merge, sort_get_sorted_hints}, + merkle_tree::MembershipWitness }; fn build_nullifier_tree() -> NonEmptyMerkleTree { diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr index a9c104a3ce7..1063374ec22 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr @@ -432,14 +432,15 @@ mod tests { prev_unencrypted_logs_hash, prev_unencrypted_log_preimages_length ); + let prev_data = builder.previous_kernel.to_public_accumulated_data(); let mut expected_unencrypted_logs = [ - builder.previous_kernel.unencrypted_logs_hashes.storage[0].log_hash, builder.public_call.public_inputs.unencrypted_logs_hashes.storage[0] + prev_data.unencrypted_logs_hashes[0], builder.public_call.public_inputs.unencrypted_logs_hashes.storage[0] ]; // silo the new log hash expected_unencrypted_logs[1].value = compute_siloed_unencrypted_log_hash(builder.public_call.contract_address, expected_unencrypted_logs[1].value); // we assume the encrypted log is already siloed from private kernels - let expected_encrypted_logs = [builder.previous_kernel.encrypted_logs_hashes.storage[0].expose_to_public()]; + let expected_encrypted_logs = [prev_data.encrypted_logs_hashes[0]]; let public_inputs = builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr index 7d05daec74a..aeb0e840409 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/tests/nullifier_non_existent_read_request_hints_builder.nr @@ -5,9 +5,8 @@ use dep::types::{ MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, NULLIFIER_TREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_HEIGHT }, - merkle_tree::MembershipWitness, - tests::{merkle_tree_utils::NonEmptyMerkleTree, sort::sort_get_sorted_hints}, - utils::{arrays::find_index, field::full_field_greater_than} + merkle_tree::MembershipWitness, tests::{merkle_tree_utils::NonEmptyMerkleTree}, + utils::{arrays::{find_index, sort_get_sorted_hints}, field::full_field_greater_than} }; struct NullifierNonExistentReadRequestHintsBuilder { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr index 98c5be1191e..56c79e0b693 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr @@ -1,29 +1,18 @@ use crate::{ - address::AztecAddress, hash::{compute_tx_logs_hash, compute_tx_note_logs_hash}, abis::{ - gas::Gas, - accumulated_data::{ - combined_accumulated_data::CombinedAccumulatedData, - private_accumulated_data::PrivateAccumulatedData, public_accumulated_data::PublicAccumulatedData, - public_accumulated_data_builder::PublicAccumulatedDataBuilder -}, - call_request::CallRequest, note_hash::{NoteHash, ScopedNoteHash}, nullifier::ScopedNullifier, - private_call_request::ScopedPrivateCallRequest, public_data_update_request::PublicDataUpdateRequest, + accumulated_data::{private_accumulated_data::PrivateAccumulatedData}, call_request::CallRequest, + note_hash::ScopedNoteHash, nullifier::ScopedNullifier, + private_call_request::ScopedPrivateCallRequest, log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash} }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX }, - messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::{Empty, is_empty} + messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::Empty }; -// Builds via PrivateKernelCircuitPublicInputsBuilder: -// .finish: PrivateKernelCircuitPublicInputs.end -// .to_combined: KernelCircuitPublicInputs.end -// .split_to_public: PublicKernelCircuitPublicInputs.(end,end_non_revertible) struct PrivateAccumulatedDataBuilder { new_note_hashes: BoundedVec, new_nullifiers: BoundedVec, @@ -51,180 +40,6 @@ impl PrivateAccumulatedDataBuilder { public_call_stack: self.public_call_stack.storage } } - - pub fn to_combined(self, teardown_gas: Gas) -> CombinedAccumulatedData { - // TODO(Miranda): Hash here or elsewhere? - let note_encrypted_logs_hash = compute_tx_note_logs_hash(self.note_encrypted_logs_hashes.storage.map(|l: NoteLogHash| l.expose_to_public())); - let encrypted_logs_hash = compute_tx_logs_hash(self.encrypted_logs_hashes.storage.map(|l: ScopedEncryptedLogHash| l.expose_to_public())); - let unencrypted_logs_hash = compute_tx_logs_hash(self.unencrypted_logs_hashes.storage.map(|l: ScopedLogHash| l.log_hash)); - let gas_used = self.to_metered_gas_used() + Gas::tx_overhead() + teardown_gas; - let note_encrypted_log_preimages_length = self.note_encrypted_logs_hashes.storage.fold(0, |a, b: NoteLogHash| a + b.length); - let encrypted_log_preimages_length = self.encrypted_logs_hashes.storage.fold(0, |a, b: ScopedEncryptedLogHash| a + b.log_hash.length); - let unencrypted_log_preimages_length = self.unencrypted_logs_hashes.storage.fold(0, |a, b: ScopedLogHash| a + b.log_hash.length); - - CombinedAccumulatedData { - new_note_hashes: self.new_note_hashes.storage.map(|n: ScopedNoteHash| n.note_hash.value), - new_nullifiers: self.new_nullifiers.storage.map(|n: ScopedNullifier| n.nullifier.value), - new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage.map(|m: ScopedL2ToL1Message| m.message.content), - note_encrypted_logs_hash, - encrypted_logs_hash, - unencrypted_logs_hash, - note_encrypted_log_preimages_length, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - public_data_update_requests: [PublicDataUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - gas_used - } - } - - pub fn to_metered_gas_used(self) -> Gas { - let mut metered_bytes = 0; - - // note_hash_gas - for i in 0..self.new_note_hashes.storage.len() { - if !is_empty(self.new_note_hashes.get_unchecked(i)) { - metered_bytes += DA_BYTES_PER_FIELD; - } - } - - // nullifier_gas - for i in 0..self.new_nullifiers.storage.len() { - if !is_empty(self.new_nullifiers.get_unchecked(i)) { - metered_bytes += DA_BYTES_PER_FIELD; - } - } - - // l2_to_l1_msg_gas - for i in 0..self.new_l2_to_l1_msgs.storage.len() { - if !is_empty(self.new_l2_to_l1_msgs.get_unchecked(i)) { - metered_bytes += DA_BYTES_PER_FIELD; - } - } - - // note_encrypted_logs_hash_gas - for i in 0..self.note_encrypted_logs_hashes.storage.len() { - let log = self.note_encrypted_logs_hashes.get_unchecked(i); - metered_bytes += log.length as u32; - } - - // encrypted_logs_hash_gas - for i in 0..self.encrypted_logs_hashes.storage.len() { - let log = self.encrypted_logs_hashes.get_unchecked(i); - metered_bytes += log.log_hash.length as u32; - } - - // unencrypted_logs_hash_gas - for i in 0..self.unencrypted_logs_hashes.storage.len() { - let log = self.unencrypted_logs_hashes.get_unchecked(i); - metered_bytes += log.log_hash.length as u32; - } - - Gas::new(DA_GAS_PER_BYTE * metered_bytes, 0) - } - - pub fn split_to_public( - self, - min_revertible_side_effect_counter: u32, - teardown_gas: Gas - ) -> (PublicAccumulatedData, PublicAccumulatedData) { - let mut non_revertible_builder = PublicAccumulatedDataBuilder::empty(); - let mut revertible_builder = PublicAccumulatedDataBuilder::empty(); - let mut non_revertible_da_gas_used = 0; - let mut non_revertible_l2_gas_used = 0; - let mut revertible_da_gas_used = teardown_gas.da_gas; // pre-pay for teardown gas - let mut revertible_l2_gas_used = teardown_gas.l2_gas; - let DA_GAS_PER_FIELD = DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE; - - for i in 0..MAX_NEW_NOTE_HASHES_PER_TX { - let note_hash = self.new_note_hashes.storage[i]; - let public_note_hash = note_hash.expose_to_public(); - if note_hash.counter() < min_revertible_side_effect_counter { - non_revertible_builder.new_note_hashes.push(public_note_hash); - if !is_empty(public_note_hash) { - non_revertible_da_gas_used += DA_GAS_PER_FIELD ; - } - } else { - revertible_builder.new_note_hashes.push(public_note_hash); - if !is_empty(public_note_hash) { - revertible_da_gas_used += DA_GAS_PER_FIELD; - } - } - } - - for i in 0..MAX_NEW_NULLIFIERS_PER_TX { - let nullifier = self.new_nullifiers.storage[i]; - let public_nullifier = nullifier.expose_to_public(); - if nullifier.counter() < min_revertible_side_effect_counter { - non_revertible_builder.new_nullifiers.push(public_nullifier); - if !is_empty(public_nullifier) { - non_revertible_da_gas_used += DA_GAS_PER_FIELD; - } - } else { - revertible_builder.new_nullifiers.push(public_nullifier); - if !is_empty(public_nullifier) { - revertible_da_gas_used += DA_GAS_PER_FIELD; - } - } - } - - for i in 0..MAX_NEW_L2_TO_L1_MSGS_PER_TX { - let msg = self.new_l2_to_l1_msgs.storage[i]; - if msg.counter() < min_revertible_side_effect_counter { - non_revertible_builder.new_l2_to_l1_msgs.push(msg.message.content); - } else { - revertible_builder.new_l2_to_l1_msgs.push(msg.message.content); - } - } - - // TODO(gas): add AVM_STARTUP_L2_GAS here - for i in 0..MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX { - let call_stack_item = self.public_call_stack.storage[i]; - if call_stack_item.start_side_effect_counter < min_revertible_side_effect_counter { - non_revertible_builder.public_call_stack.push(call_stack_item); - } else { - revertible_builder.public_call_stack.push(call_stack_item); - } - } - - for i in 0..MAX_NOTE_ENCRYPTED_LOGS_PER_TX { - let note_encrypted_logs_hash = self.note_encrypted_logs_hashes.storage[i]; - let note_encrypted_logs_hash_public = note_encrypted_logs_hash.expose_to_public(); - if note_encrypted_logs_hash.counter < min_revertible_side_effect_counter { - non_revertible_builder.note_encrypted_logs_hashes.push(note_encrypted_logs_hash_public); - non_revertible_da_gas_used += note_encrypted_logs_hash_public.length as u32 * DA_GAS_PER_BYTE; - } else { - revertible_builder.note_encrypted_logs_hashes.push(note_encrypted_logs_hash_public); - revertible_da_gas_used += note_encrypted_logs_hash_public.length as u32 * DA_GAS_PER_BYTE; - } - } - - for i in 0..MAX_ENCRYPTED_LOGS_PER_TX { - let encrypted_logs_hash = self.encrypted_logs_hashes.storage[i]; - let encrypted_logs_hash_public = encrypted_logs_hash.expose_to_public(); - if encrypted_logs_hash.counter() < min_revertible_side_effect_counter { - non_revertible_builder.encrypted_logs_hashes.push(encrypted_logs_hash_public); - non_revertible_da_gas_used += encrypted_logs_hash_public.length as u32 * DA_GAS_PER_BYTE; - } else { - revertible_builder.encrypted_logs_hashes.push(encrypted_logs_hash_public); - revertible_da_gas_used += encrypted_logs_hash_public.length as u32 * DA_GAS_PER_BYTE; - } - } - - for i in 0..MAX_UNENCRYPTED_LOGS_PER_TX { - let unencrypted_logs_hash = self.unencrypted_logs_hashes.storage[i].log_hash; - if unencrypted_logs_hash.counter < min_revertible_side_effect_counter { - non_revertible_builder.unencrypted_logs_hashes.push(unencrypted_logs_hash); - non_revertible_da_gas_used += unencrypted_logs_hash.length as u32 * DA_GAS_PER_BYTE; - } else { - revertible_builder.unencrypted_logs_hashes.push(unencrypted_logs_hash); - revertible_da_gas_used += unencrypted_logs_hash.length as u32 * DA_GAS_PER_BYTE; - } - } - - revertible_builder.gas_used = Gas::new(revertible_da_gas_used, revertible_l2_gas_used); - non_revertible_builder.gas_used = Gas::tx_overhead() + Gas::new(non_revertible_da_gas_used, non_revertible_l2_gas_used); - (non_revertible_builder.finish(), revertible_builder.finish()) - } } impl Empty for PrivateAccumulatedDataBuilder { @@ -241,233 +56,3 @@ impl Empty for PrivateAccumulatedDataBuilder { } } } - -mod tests { - use crate::{ - abis::{ - accumulated_data::private_accumulated_data_builder::PrivateAccumulatedDataBuilder, gas::Gas, - call_request::CallRequest, caller_context::CallerContext, note_hash::NoteHash, - nullifier::Nullifier, public_data_update_request::PublicDataUpdateRequest, - log_hash::{LogHash, NoteLogHash, ScopedLogHash, EncryptedLogHash, ScopedEncryptedLogHash} - }, - address::{AztecAddress, EthAddress}, messaging::l2_to_l1_message::L2ToL1Message, - utils::arrays::array_eq, constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE} - }; - - #[test] - unconstrained fn splits_revertible_and_non_revertible() { - let mut builder = PrivateAccumulatedDataBuilder::empty(); - let contract_address = AztecAddress::from_field(8989); - - let min_revertible_side_effect_counter = 13; - - // Non revertible: counter < 13 - - let non_revertible_note_hashes = [ - NoteHash { value: 1, counter: 1 }.scope(20, contract_address), - NoteHash { value: 2, counter: 4 }.scope(5, contract_address) - ]; - - let non_revertible_note_logs = [NoteLogHash { value: 11, counter: 2, length: 2, note_hash_counter: 1 }]; - - let non_revertible_nullifiers = [ - Nullifier { value: 10, note_hash: 1, counter: 3 }.scope(contract_address), - Nullifier { value: 20, note_hash: 2, counter: 5 }.scope(contract_address) - ]; - - let non_revertible_l2_to_l1_messages = [ - L2ToL1Message { recipient: EthAddress::from_field(3030), content: 333333, counter: 6 }.scope(AztecAddress::from_field(9900)) - ]; - - let non_revertible_public_stack = [ - CallRequest { - hash: 1, - caller_contract_address: AztecAddress::from_field(1), - caller_context: CallerContext::empty(), - start_side_effect_counter: 6, - end_side_effect_counter: 0 - }, - CallRequest { - hash: 2, - caller_contract_address: AztecAddress::from_field(1), - caller_context: CallerContext::empty(), - start_side_effect_counter: 7, - end_side_effect_counter: 0 - } - ]; - - let non_revertible_enc_log_hashes = [ - EncryptedLogHash { value: 11, counter: 9, length: 2, randomness: 4 }.scope(contract_address), - EncryptedLogHash { value: 22, counter: 10, length: 2, randomness: 4 }.scope(contract_address) - ]; - - let non_revertible_unenc_log_hashes = [ - LogHash { value: 33, counter: 11, length: 5 }.scope(contract_address), - LogHash { value: 44, counter: 12, length: 5 }.scope(contract_address) - ]; - - // Revertible: counter >= 13 - - let revertible_note_hashes = [ - NoteHash { value: 3, counter: 13 }.scope(15, contract_address), - NoteHash { value: 4, counter: 16 }.scope(0, contract_address) - ]; - - let revertible_note_logs = [NoteLogHash { value: 33, counter: 14, length: 2, note_hash_counter: 13 }]; - - let revertible_nullifiers = [ - Nullifier { value: 30, note_hash: 3, counter: 15 }.scope(contract_address), - Nullifier { value: 40, note_hash: 4, counter: 18 }.scope(contract_address) - ]; - - let revertible_l2_to_l1_messages = [ - L2ToL1Message { recipient: EthAddress::from_field(3030), content: 444444, counter: 19 }.scope(AztecAddress::from_field(7788)) - ]; - - let revertible_public_call_stack = [ - CallRequest { - hash: 3, - caller_contract_address: AztecAddress::from_field(3), - caller_context: CallerContext::empty(), - start_side_effect_counter: 17, - end_side_effect_counter: 0 - } - ]; - - let revertible_enc_log_hashes = [ - EncryptedLogHash { value: 55, counter: 20, length: 2, randomness: 4 }.scope(contract_address), - EncryptedLogHash { value: 66, counter: 21, length: 2, randomness: 4 }.scope(contract_address) - ]; - - let revertible_unenc_log_hashes = [ - LogHash { value: 77, counter: 22, length: 5 }.scope(contract_address), - LogHash { value: 88, counter: 23, length: 5 }.scope(contract_address) - ]; - - builder.new_note_hashes.extend_from_array(non_revertible_note_hashes); - builder.new_note_hashes.extend_from_array(revertible_note_hashes); - - builder.new_nullifiers.extend_from_array(non_revertible_nullifiers); - builder.new_nullifiers.extend_from_array(revertible_nullifiers); - - builder.new_l2_to_l1_msgs.extend_from_array(non_revertible_l2_to_l1_messages); - builder.new_l2_to_l1_msgs.extend_from_array(revertible_l2_to_l1_messages); - - builder.public_call_stack.extend_from_array(non_revertible_public_stack); - builder.public_call_stack.extend_from_array(revertible_public_call_stack); - - builder.note_encrypted_logs_hashes.extend_from_array(non_revertible_note_logs); - builder.note_encrypted_logs_hashes.extend_from_array(revertible_note_logs); - - builder.encrypted_logs_hashes.extend_from_array(non_revertible_enc_log_hashes); - builder.encrypted_logs_hashes.extend_from_array(revertible_enc_log_hashes); - - builder.unencrypted_logs_hashes.extend_from_array(non_revertible_unenc_log_hashes); - builder.unencrypted_logs_hashes.extend_from_array(revertible_unenc_log_hashes); - - let public_non_revertible_note_logs = non_revertible_note_logs.map(|n: NoteLogHash| n.expose_to_public()); - let public_revertible_note_logs = revertible_note_logs.map(|n: NoteLogHash| n.expose_to_public()); - - let (non_revertible, revertible) = builder.split_to_public(min_revertible_side_effect_counter, Gas::new(42, 17)); - - assert( - array_eq( - non_revertible.new_note_hashes, - [ - NoteHash { value: 1, counter: 0 }, - NoteHash { value: 2, counter: 0 } - ] - ) - ); - assert( - array_eq( - non_revertible.new_nullifiers, - [ - Nullifier { value: 10, note_hash: 0, counter: 0 }, - Nullifier { value: 20, note_hash: 0, counter: 0 } - ] - ) - ); - assert(array_eq(non_revertible.new_l2_to_l1_msgs, [333333])); - assert(array_eq(non_revertible.public_call_stack, non_revertible_public_stack)); - assert( - array_eq( - non_revertible.note_encrypted_logs_hashes, - public_non_revertible_note_logs - ) - ); - assert( - array_eq( - non_revertible.encrypted_logs_hashes, - non_revertible_enc_log_hashes.map(|h: ScopedEncryptedLogHash| h.expose_to_public()) - ) - ); - assert( - array_eq( - non_revertible.unencrypted_logs_hashes, - non_revertible_unenc_log_hashes.map(|h: ScopedLogHash| h.log_hash) - ) - ); - - assert( - array_eq( - revertible.new_note_hashes, - [ - NoteHash { value: 3, counter: 0 }, - NoteHash { value: 4, counter: 0 } - ] - ) - ); - assert( - array_eq( - revertible.new_nullifiers, - [ - Nullifier { value: 30, note_hash: 0, counter: 0 }, - Nullifier { value: 40, note_hash: 0, counter: 0 } - ] - ) - ); - assert(array_eq(revertible.new_l2_to_l1_msgs, [444444])); - assert(array_eq(revertible.public_call_stack, revertible_public_call_stack)); - assert( - array_eq( - revertible.note_encrypted_logs_hashes, - public_revertible_note_logs - ) - ); - assert( - array_eq( - revertible.encrypted_logs_hashes, - revertible_enc_log_hashes.map(|h: ScopedEncryptedLogHash| h.expose_to_public()) - ) - ); - assert( - array_eq( - revertible.unencrypted_logs_hashes, - revertible_unenc_log_hashes.map(|h: ScopedLogHash| h.log_hash) - ) - ); - - assert_eq( - revertible.gas_used, Gas::new( - (4 * DA_BYTES_PER_FIELD - + 6 // revertible encrypted logs len - + 10) // revertible unencrypted logs len - * DA_GAS_PER_BYTE, - 0 - ) - + Gas::new(42, 17) - ); - - assert_eq( - non_revertible.gas_used, Gas::new( - (4 * DA_BYTES_PER_FIELD - + 6 // non-revertible encrypted logs len - + 10) // non-revertible unencrypted logs len - * DA_GAS_PER_BYTE, - 0 - ) - + Gas::tx_overhead() - ); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/call_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/call_request.nr index db0f8a4d2db..2f2ee96ba7d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/call_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/call_request.nr @@ -39,6 +39,18 @@ impl Empty for CallRequest { } } +impl CallRequest { + pub fn expose_to_public(self) -> Self { + CallRequest { + hash: self.hash, + caller_contract_address: self.caller_contract_address, + caller_context: self.caller_context, + start_side_effect_counter: 0, + end_side_effect_counter: 0 + } + } +} + impl Serialize for CallRequest { fn serialize(self) -> [Field; CALL_REQUEST_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr index d0583d1661e..c718b02430f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr @@ -1,22 +1,13 @@ use crate::{ abis::{ accumulated_data::PrivateAccumulatedDataBuilder, combined_constant_data::CombinedConstantData, - kernel_circuit_public_inputs::{ - kernel_circuit_public_inputs::KernelCircuitPublicInputs, - private_kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - public_kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs -}, - gas::Gas, validation_requests::validation_requests_builder::ValidationRequestsBuilder, + kernel_circuit_public_inputs::private_kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + validation_requests::validation_requests_builder::ValidationRequestsBuilder, call_request::CallRequest }, - address::AztecAddress, constants::MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - partial_state_reference::PartialStateReference, traits::{Empty, is_empty} + address::AztecAddress, traits::Empty }; -// Builds: -// .finish: PrivateKernelCircuitPublicInputs -// .finish_tail: KernelCircuitPublicInputs (from KernelCircuitPublicInputsComposer) -// .finish_to_public: PublicKernelCircuitPublicInputs (from KernelCircuitPublicInputsComposer) struct PrivateKernelCircuitPublicInputsBuilder { min_revertible_side_effect_counter: u32, validation_requests: ValidationRequestsBuilder, @@ -37,45 +28,12 @@ impl PrivateKernelCircuitPublicInputsBuilder { fee_payer: self.fee_payer } } - - pub fn finish_tail(self, teardown_gas: Gas) -> KernelCircuitPublicInputs { - KernelCircuitPublicInputs { - rollup_validation_requests: self.validation_requests.to_rollup(), - end: self.end.to_combined(teardown_gas), - constants: self.constants, - start_state: PartialStateReference::empty(), - revert_code: 0, - fee_payer: self.fee_payer - } - } - - pub fn finish_to_public( - self, - teardown_gas: Gas, - min_revertible_side_effect_counter: u32 - ) -> PublicKernelCircuitPublicInputs { - let (end_non_revertible, end) = self.end.split_to_public(min_revertible_side_effect_counter, teardown_gas); - let mut public_teardown_call_stack: BoundedVec = BoundedVec::new(); - if (!is_empty(self.public_teardown_call_request)) { - public_teardown_call_stack.push(self.public_teardown_call_request); - } - - PublicKernelCircuitPublicInputs { - validation_requests: self.validation_requests.finish(), - end_non_revertible, - end, - constants: self.constants, - revert_code: 0, - public_teardown_call_stack: public_teardown_call_stack.storage, - fee_payer: self.fee_payer - } - } } impl Empty for PrivateKernelCircuitPublicInputsBuilder { fn empty() -> Self { PrivateKernelCircuitPublicInputsBuilder { - min_revertible_side_effect_counter: 0 as u32, + min_revertible_side_effect_counter: 0, validation_requests: ValidationRequestsBuilder::empty(), end: PrivateAccumulatedDataBuilder::empty(), constants: CombinedConstantData::empty(), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/log_hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/log_hash.nr index c212bd06385..6d5ad39702f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/log_hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/log_hash.nr @@ -131,6 +131,14 @@ impl Deserialize for ScopedLogHash { } } +impl ScopedLogHash { + pub fn expose_to_public(self) -> LogHash { + // Hide the counter when exposing to public. + // The log hash must already be siloed when we call this. + LogHash { value: self.log_hash.value, counter: 0, length: self.log_hash.length } + } +} + struct EncryptedLogHash { value: Field, counter: u32, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 34d249c9c12..bf4a9151288 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -2,7 +2,10 @@ use crate::{ abis::{ gas::Gas, gas_settings::GasSettings, call_context::CallContext, call_request::{CallerContext, CallRequest}, - accumulated_data::{CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PublicAccumulatedData}, + accumulated_data::{ + CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, + PublicAccumulatedData, PublicAccumulatedDataBuilder +}, function_data::FunctionData, global_variables::GlobalVariables, combined_constant_data::CombinedConstantData, kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, @@ -283,8 +286,8 @@ impl FixtureBuilder { } } - pub fn to_private_accumulated_data(self) -> PrivateAccumulatedData { - let public_inputs = PrivateAccumulatedDataBuilder { + pub fn to_private_accumulated_data_builder(self) -> PrivateAccumulatedDataBuilder { + PrivateAccumulatedDataBuilder { new_note_hashes: self.new_note_hashes, new_nullifiers: self.new_nullifiers, new_l2_to_l1_msgs: self.new_l2_to_l1_msgs, @@ -293,19 +296,67 @@ impl FixtureBuilder { unencrypted_logs_hashes: self.unencrypted_logs_hashes, private_call_stack: vec_reverse(self.private_call_requests), public_call_stack: self.public_call_requests + } + } + + pub fn to_private_accumulated_data(self) -> PrivateAccumulatedData { + self.to_private_accumulated_data_builder().finish() + } + + pub fn to_public_accumulated_data_builder(self) -> PublicAccumulatedDataBuilder { + let new_note_hashes = BoundedVec { + storage: self.new_note_hashes.storage.map(|n: ScopedNoteHash| n.note_hash), + len: self.new_note_hashes.len() + }; + let new_nullifiers = BoundedVec { + storage: self.new_nullifiers.storage.map(|n: ScopedNullifier| n.nullifier), + len: self.new_nullifiers.len() + }; + let new_l2_to_l1_msgs = BoundedVec { + storage: self.new_l2_to_l1_msgs.storage.map(|m: ScopedL2ToL1Message| m.message.content), + len: self.new_l2_to_l1_msgs.len() + }; + let note_encrypted_logs_hashes = BoundedVec { + storage: self.note_encrypted_logs_hashes.storage.map(|l: NoteLogHash| LogHash { value: l.value, counter: l.counter, length: l.length }), + len: self.note_encrypted_logs_hashes.len() + }; + let encrypted_logs_hashes = BoundedVec { + storage: self.encrypted_logs_hashes.storage.map(|l: ScopedEncryptedLogHash| l.log_hash).map(|l: EncryptedLogHash| LogHash { value: l.value, counter: l.counter, length: l.length }), + len: self.encrypted_logs_hashes.len() + }; + let unencrypted_logs_hashes = BoundedVec { + storage: self.unencrypted_logs_hashes.storage.map(|l: ScopedLogHash| l.log_hash), + len: self.unencrypted_logs_hashes.len() }; - public_inputs.finish() + + PublicAccumulatedDataBuilder { + new_note_hashes, + new_nullifiers, + new_l2_to_l1_msgs, + note_encrypted_logs_hashes, + encrypted_logs_hashes, + unencrypted_logs_hashes, + public_data_update_requests: self.public_data_update_requests, + public_call_stack: self.public_call_requests, + gas_used: self.gas_used + } } pub fn to_public_accumulated_data(self) -> PublicAccumulatedData { + self.to_public_accumulated_data_builder().finish() + } + + pub fn to_exposed_public_accumulated_data(self) -> PublicAccumulatedData { PublicAccumulatedData { - new_note_hashes: self.new_note_hashes.storage.map(|n: ScopedNoteHash| n.note_hash), - new_nullifiers: self.new_nullifiers.storage.map(|n: ScopedNullifier| n.nullifier), + new_note_hashes: self.new_note_hashes.storage.map(|n: ScopedNoteHash| n.expose_to_public()), + new_nullifiers: self.new_nullifiers.storage.map(|n: ScopedNullifier| n.expose_to_public()), new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage.map(|m: ScopedL2ToL1Message| m.message.content), note_encrypted_logs_hashes: self.note_encrypted_logs_hashes.storage.map(|l: NoteLogHash| l.expose_to_public()), encrypted_logs_hashes: self.encrypted_logs_hashes.storage.map(|l: ScopedEncryptedLogHash| l.expose_to_public()), - unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage.map(|l: ScopedLogHash| l.log_hash), + unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage.map(|l: ScopedLogHash| l.expose_to_public()), public_data_update_requests: self.public_data_update_requests.storage, + // TODO: Hide the counter of a public call request. + // public_call_stack: self.public_call_requests.storage.map(|cr: CallRequest| cr.expose_to_public()), public_call_stack: self.public_call_requests.storage, gas_used: self.gas_used } @@ -361,6 +412,7 @@ impl FixtureBuilder { } pub fn to_public_kernel_circuit_public_inputs(self, revertible: bool) -> PublicKernelCircuitPublicInputs { + // TODO: Split the data using self.min_revertible_side_effect_counter. let accumulated_data = self.to_public_accumulated_data(); let end_non_revertible = if revertible { PublicAccumulatedData::empty() @@ -470,6 +522,13 @@ impl FixtureBuilder { } } + pub fn set_first_nullifier(&mut self) { + assert_eq(self.new_nullifiers.len(), 0, "first nullifier already set"); + let value = self.mock_nullifier_value(0); + let first_nullifier = Nullifier { value, counter: 0, note_hash: 0 }.scope(AztecAddress::zero()); + self.new_nullifiers.push(first_nullifier); + } + pub fn add_nullifier(&mut self, value: Field) { self.new_nullifiers.push( Nullifier { value, counter: self.next_counter(), note_hash: 0 }.scope(self.storage_contract_address) @@ -661,6 +720,13 @@ impl FixtureBuilder { self.encrypted_log_preimages_length += length; } + pub fn add_siloed_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.log_hash.value = silo_encrypted_log_hash(log_hash); + self.encrypted_logs_hashes.push(log_hash); + self.encrypted_log_preimages_length += length; + } + pub fn append_encrypted_log_hashes(&mut self, num: u32) { let index_offset = self.encrypted_logs_hashes.len(); for i in 0..self.encrypted_logs_hashes.max_len() { @@ -688,6 +754,13 @@ impl FixtureBuilder { self.unencrypted_log_preimages_length += length; } + pub fn add_siloed_unencrypted_log_hash(&mut self, hash: Field, length: Field) { + let mut log_hash = LogHash { value: hash, counter: self.next_counter(), length }.scope(self.storage_contract_address); + log_hash.log_hash.value = silo_unencrypted_log_hash(log_hash); + self.unencrypted_logs_hashes.push(log_hash); + self.unencrypted_log_preimages_length += length; + } + pub fn append_unencrypted_log_hashes(&mut self, num: u32) { let index_offset = self.unencrypted_logs_hashes.len(); for i in 0..self.unencrypted_logs_hashes.max_len() { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/sort.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/sort.nr index 506bacf0bbe..e3cbe0c4918 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/sort.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/sort.nr @@ -1,14 +1,9 @@ -use crate::traits::{Empty, is_empty}; - struct SortedTuple { value: T, original_index: u32, } -pub fn sort_high_to_low( - values: [T; N], - is_less_than: fn(T, T) -> bool -) -> [SortedTuple; N] where T: Eq { +pub fn sort_high_to_low(values: [T; N], is_less_than: fn(T, T) -> bool) -> [SortedTuple; N] where T: Eq { let mut sorted_tuples = [SortedTuple { value: values[0], original_index: 0 }; N]; for i in 0..N { @@ -20,68 +15,3 @@ pub fn sort_high_to_low( sorted_tuples.sort_via(|a: SortedTuple, b: SortedTuple| (b.value == a.value) | is_less_than(b.value, a.value)) } - -struct SortedResult { - sorted_array: [T; N], - sorted_index_hints: [u32; N], -} - -pub fn sort_get_sorted_hints( - values: [T; N], - ordering: fn(T, T) -> bool -) -> SortedResult where T: Eq + Empty { - let mut tuples = [SortedTuple { value: values[0], original_index: 0 }; N]; - for i in 0..N { - tuples[i] = SortedTuple { - value: values[i], - original_index: i, - }; - } - - let sorted_tuples = tuples.sort_via( - |a: SortedTuple, b: SortedTuple| is_empty(b.value) | (!is_empty(a.value) & !is_empty(b.value) & ordering(a.value, b.value)) - ); - - let sorted_array = sorted_tuples.map(|t: SortedTuple| t.value); - let mut sorted_index_hints = [0; N]; - for i in 0..N { - if !is_empty(sorted_tuples[i].value) { - let original_index = sorted_tuples[i].original_index; - sorted_index_hints[original_index] = i; - } - } - - SortedResult { sorted_array, sorted_index_hints } -} - -#[test] -fn sort_get_sorted_hints_asc_non_padded() { - let values = [40, 60, 20, 50]; - let res = sort_get_sorted_hints(values, |a: Field, b: Field| a.lt(b)); - assert_eq(res.sorted_array, [20, 40, 50, 60]); - assert_eq(res.sorted_index_hints, [1, 3, 0, 2]); -} - -#[test] -fn sort_get_sorted_hints_desc_non_padded() { - let values = [40, 20, 60, 50]; - let res = sort_get_sorted_hints(values, |a: Field, b: Field| b.lt(a)); - assert_eq(res.sorted_array, [60, 50, 40, 20]); - assert_eq(res.sorted_index_hints, [2, 3, 0, 1]); -} - -#[test] -fn sort_get_sorted_hints_asc_padded() { - let values = [40, 60, 20, 50, 0, 0]; - let res = sort_get_sorted_hints(values, |a: Field, b: Field| a.lt(b)); - assert_eq(res.sorted_array, [20, 40, 50, 60, 0, 0]); - assert_eq(res.sorted_index_hints, [1, 3, 0, 2, 0, 0]); -} - -#[test] -fn sort_get_sorted_hints_desc_padded() { - let values = [40, 20, 60, 50, 0, 0]; - let res = sort_get_sorted_hints(values, |a: Field, b: Field| b.lt(a)); - assert_eq(res.sorted_array, [60, 50, 40, 20, 0, 0]); - assert_eq(res.sorted_index_hints, [2, 3, 0, 1, 0, 0]); -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr index 711141627c7..bc95d59c4cb 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/utils.nr @@ -17,3 +17,11 @@ pub fn assert_array_eq(array: [T; N], expected: [T; S]) where T: Empty assert_eq(array[i], expected[i], "mismatch array elements"); } } + +// Swap two items in a BoundedVec. +// Useful when we want to shuffle side effects, which by default are ordered by counters when we add mock data to FixtureBuilder. +pub fn swap_items(vec: &mut BoundedVec, from_index: u64, to_index: u64) { + let tmp = vec.storage[from_index]; + vec.storage[from_index] = vec.storage[to_index]; + vec.storage[to_index] = tmp; +} 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 ba638d16e2d..179d1f893f3 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,14 +1,20 @@ mod assert_sorted_array; mod assert_sorted_transformed_value_array; +mod assert_split_sorted_transformed_value_arrays; mod sort_by_counters; mod sort_get_order_hints; +mod sort_get_sorted_hints; mod sort_get_sorted_tuple; +mod sort_get_split_order_hints; // Re-exports. 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 sort_by_counters::{sort_by_counters_asc, sort_by_counters_desc}; -use sort_get_order_hints::{OrderHint, sort_get_order_hints_asc}; +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; +use sort_get_split_order_hints::{sort_get_split_order_hints_asc, sort_get_split_order_hints_desc, SplitOrderHints}; use crate::traits::{Empty, is_empty}; use crate::abis::side_effect::{Positioned, Ordered}; 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 1de3ea15a0a..c07e2d54c43 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,7 +1,8 @@ use crate::{abis::side_effect::Ordered, traits::{Empty, is_empty}, utils::arrays::sort_get_order_hints::OrderHint}; // original_array must be valid, i.e. validate_array(original_array) == true -pub fn assert_sorted_transformed_value_array( +// transformed_value_array must be verified against original_array before calling this function. +pub fn assert_sorted_transformed_value_array( original_array: [T; N], transformed_value_array: [S; N], sorted_transformed_value_array: [S; N], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr new file mode 100644 index 00000000000..e977ad2fc03 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_split_sorted_transformed_value_arrays.nr @@ -0,0 +1,543 @@ +use crate::{ + abis::side_effect::Ordered, traits::{Empty, is_empty}, + utils::arrays::{array_length, sort_get_split_order_hints::SplitOrderHints, validate_array} +}; + +// 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. +fn assert_split_sorted_transformed_value_arrays( + original_array: [T; N], + transformed_value_array: [S; N], + split_counter: u32, + sorted_transformed_value_array_lt: [S; N], // Values whose counters are less than the split counter. + sorted_transformed_value_array_gte: [S; N], // Values whose counters are greater than or equal to the split counter. + sorted_counters_lt: [u32; N], // Counters of the values in sorted_transformed_value_array_lt. + sorted_counters_gte: [u32; N], // Counters of the values in sorted_transformed_value_array_gte. + index_hints: [u32; N], // The index of the item in the correspinding sorted_transformed_value_array_(lt/gte) for each item in the original_array. + ascending: bool // Whether the items in sorted_transformed_value_array_(lt/gte) is in ascending order. +) where T: Ordered + Empty + Eq, S: Empty + Eq { + // Can use array_length instead of validate_array for the original_array because it's taken from the previous kernel and guaranteed to be valid. + let total_num = array_length(original_array); + + let mut num_lt = 0; + let mut num_gte = 0; + let mut should_check = true; + for i in 0..N { + should_check &= i != total_num; + if should_check { + let original = original_array[i]; + let value = transformed_value_array[i]; + let sorted_index = index_hints[i]; + let is_lt = (original.counter() < split_counter) | (original.counter() == 0); // If counter is 0, the value should always be in the lt(non-revertible) set. Currently this only applies to the first nullifier. + let mut sorted_array = sorted_transformed_value_array_gte; + let mut sorted_counters = sorted_counters_gte; + let mut num = num_gte; + if is_lt { + sorted_array = sorted_transformed_value_array_lt; + sorted_counters = sorted_counters_lt; + num = num_lt; + }; + assert_eq(value, sorted_array[sorted_index], "mismatch sorted values"); + assert_eq(original.counter(), sorted_counters[sorted_index], "mismatch counters"); + if num != 0 { + let is_incrementing = sorted_counters[num] > sorted_counters[num - 1]; + assert(ascending == is_incrementing, "value array must be sorted by counter"); + assert(sorted_counters[num] != sorted_counters[num - 1], "counters must not be the same"); + } + if is_lt { + num_lt += 1; + } else { + num_gte += 1; + } + } + } + + let num_non_empty_values_lt = validate_array(sorted_transformed_value_array_lt); + assert_eq(num_non_empty_values_lt, num_lt, "mismatch number of values lt"); + + let num_non_empty_values_gte = validate_array(sorted_transformed_value_array_gte); + assert_eq(num_non_empty_values_gte, num_gte, "mismatch number of values gte"); +} + +pub fn assert_split_sorted_transformed_value_arrays_asc( + original_array: [T; N], + transformed_value_array: [S; N], + split_counter: u32, + sorted_transformed_value_array_lt: [S; N], + sorted_transformed_value_array_gte: [S; N], + hints: SplitOrderHints +) where T: Ordered + Empty + Eq, S: Empty + Eq { + assert_split_sorted_transformed_value_arrays( + original_array, + transformed_value_array, + split_counter, + sorted_transformed_value_array_lt, + sorted_transformed_value_array_gte, + hints.sorted_counters_lt, + hints.sorted_counters_gte, + hints.sorted_indexes, + true + ); +} + +pub fn assert_split_sorted_transformed_value_arrays_desc( + original_array: [T; N], + transformed_value_array: [S; N], + split_counter: u32, + sorted_transformed_value_array_lt: [S; N], + sorted_transformed_value_array_gte: [S; N], + hints: SplitOrderHints +) where T: Ordered + Empty + Eq, S: Empty + Eq { + assert_split_sorted_transformed_value_arrays( + original_array, + transformed_value_array, + split_counter, + sorted_transformed_value_array_lt, + sorted_transformed_value_array_gte, + hints.sorted_counters_lt, + hints.sorted_counters_gte, + hints.sorted_indexes, + false + ); +} + +mod tests { + use crate::{ + abis::side_effect::Ordered, traits::Empty, + utils::arrays::{ + assert_split_sorted_transformed_value_arrays::{assert_split_sorted_transformed_value_arrays_asc, assert_split_sorted_transformed_value_arrays_desc}, + sort_get_split_order_hints::SplitOrderHints + } + }; + + 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 transform_value(item: TestItem) -> TestValue { + TestValue { name: item.name, total: item.price + item.tax } + } + + global original_array = [ + TestItem { name: 0, price: 1, tax: 0, counter: 33 }, + TestItem { name: 1, price: 10, tax: 6, counter: 44 }, + TestItem { name: 2, price: 20, tax: 7, counter: 11 }, + TestItem { name: 3, price: 30, tax: 8, counter: 0 }, + TestItem { name: 4, price: 40, tax: 9, counter: 22 }, + TestItem::empty(), + TestItem::empty(), + TestItem::empty() + ]; + + struct TestDataBuilder { + original_array: [T; N], + transformed_value_array: [S; N], + sorted_transformed_value_array_lt: [S; N], + sorted_transformed_value_array_gte: [S; N], + split_counter: u32, + hints: SplitOrderHints, + ascending: bool, + } + + impl TestDataBuilder { + pub fn empty() -> Self { + TestDataBuilder { + original_array: [TestItem::empty(); 8], + transformed_value_array: [TestValue::empty(); 8], + sorted_transformed_value_array_lt: [TestValue::empty(); 8], + sorted_transformed_value_array_gte: [TestValue::empty(); 8], + split_counter: 0, + hints: SplitOrderHints::empty(), + ascending: false + } + } + + pub fn new() -> Self { + let transformed_value_array = original_array.map(|item: TestItem| transform_value(item)); + + let split_counter = 15; + let sorted_transformed_value_array_lt = [ + TestValue { name: 3, total: 38 }, + TestValue { name: 2, total: 27 }, + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty() + ]; + let sorted_transformed_value_array_gte = [ + TestValue { name: 4, total: 49 }, + TestValue { name: 0, total: 1 }, + TestValue { name: 1, total: 16 }, + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty() + ]; + let hints = SplitOrderHints { + sorted_counters_lt: [0, 11, 0, 0, 0, 0, 0, 0], + sorted_counters_gte: [22, 33, 44, 0, 0, 0, 0, 0], + sorted_indexes: [1, 2, 1, 0, 0, 0, 0, 0] + }; + + TestDataBuilder { + original_array, + transformed_value_array, + sorted_transformed_value_array_lt, + sorted_transformed_value_array_gte, + split_counter, + hints, + ascending: true + } + } + + pub fn new_desc() -> Self { + let transformed_value_array = original_array.map(|item: TestItem| transform_value(item)); + + let split_counter = 15; + let sorted_transformed_value_array_lt = [ + TestValue { name: 2, total: 27 }, + TestValue { name: 3, total: 38 }, + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty() + ]; + let sorted_transformed_value_array_gte = [ + TestValue { name: 1, total: 16 }, + TestValue { name: 0, total: 1 }, + TestValue { name: 4, total: 49 }, + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty() + ]; + let hints = SplitOrderHints { + sorted_counters_lt: [11, 0, 0, 0, 0, 0, 0, 0], + sorted_counters_gte: [44, 33, 22, 0, 0, 0, 0, 0], + sorted_indexes: [1, 0, 0, 1, 2, 0, 0, 0] + }; + + TestDataBuilder { + original_array, + transformed_value_array, + sorted_transformed_value_array_lt, + sorted_transformed_value_array_gte, + split_counter, + hints, + ascending: false + } + } + + pub fn with_zero_split_counter(&mut self) -> Self { + self.split_counter = 0; + + if self.ascending { + self.sorted_transformed_value_array_gte = [ + TestValue { name: 2, total: 27 }, + TestValue { name: 4, total: 49 }, + TestValue { name: 0, total: 1 }, + TestValue { name: 1, total: 16 }, + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty() + ]; + self.hints = SplitOrderHints { + sorted_counters_lt: [0; 8], + sorted_counters_gte: [11, 22, 33, 44, 0, 0, 0, 0], + sorted_indexes: [2, 3, 0, 0, 1, 0, 0, 0] + }; + } else { + self.sorted_transformed_value_array_gte = [ + TestValue { name: 1, total: 16 }, + TestValue { name: 0, total: 1 }, + TestValue { name: 4, total: 49 }, + TestValue { name: 2, total: 27 }, + TestValue::empty(), + TestValue::empty(), + TestValue::empty(), + TestValue::empty() + ]; + self.hints = SplitOrderHints { + sorted_counters_lt: [0; 8], + sorted_counters_gte: [44, 33, 22, 11, 0, 0, 0, 0], + sorted_indexes: [1, 0, 3, 0, 2, 0, 0, 0], + }; + } + + self.sorted_transformed_value_array_lt = [TestValue::empty(); 8]; + // The item with 0 counter should always be in the _lt array. + self.sorted_transformed_value_array_lt[0] = TestValue { name: 3, total: 38 }; + + *self + } + + pub fn update_sorted_index(&mut self, counter: u32, new_index: u32) { + let mut original_index = original_array.len(); + for i in 0..original_array.len() { + if (original_index == original_array.len()) & (original_array[i].counter == counter) { + original_index = i; + } + } + self.hints.sorted_indexes[original_index] = new_index; + } + + pub fn execute(self) { + if self.ascending { + assert_split_sorted_transformed_value_arrays_asc( + self.original_array, + self.transformed_value_array, + self.split_counter, + self.sorted_transformed_value_array_lt, + self.sorted_transformed_value_array_gte, + self.hints + ); + } else { + assert_split_sorted_transformed_value_arrays_desc( + self.original_array, + self.transformed_value_array, + self.split_counter, + self.sorted_transformed_value_array_lt, + self.sorted_transformed_value_array_gte, + self.hints + ); + } + } + } + + /** + * asc + */ + + #[test] + fn assert_split_sorted_transformed_value_array_asc_non_zero_split_counter_succeeds() { + let builder = TestDataBuilder::new(); + builder.execute(); + } + + #[test] + fn assert_split_sorted_transformed_value_array_asc_zero_split_counter_succeeds() { + let builder = TestDataBuilder::new().with_zero_split_counter(); + builder.execute(); + } + + #[test] + fn assert_split_sorted_transformed_value_array_asc_empty_succeeds() { + let builder = TestDataBuilder::empty(); + builder.execute(); + } + + /** + * desc + */ + + #[test] + fn assert_split_sorted_transformed_value_array_desc_non_zero_split_counter_succeeds() { + let builder = TestDataBuilder::new_desc(); + builder.execute(); + } + + #[test] + fn assert_split_sorted_transformed_value_array_desc_zero_split_counter_succeeds() { + let builder = TestDataBuilder::new_desc().with_zero_split_counter(); + builder.execute(); + } + + /** + * Failed cases. + */ + + #[test(should_fail_with="mismatch sorted values")] + fn assert_split_sorted_transformed_value_array_asc_lt_wrong_sorted_value_fails() { + let mut builder = TestDataBuilder::new(); + + builder.sorted_transformed_value_array_lt[0].total += 1; + + builder.execute(); + } + + #[test(should_fail_with="mismatch sorted values")] + fn assert_split_sorted_transformed_value_array_asc_gte_wrong_sorted_value_fails() { + let mut builder = TestDataBuilder::new(); + + // Swap two values in the sorted array. + let tmp = builder.sorted_transformed_value_array_gte[0]; + builder.sorted_transformed_value_array_gte[0] = builder.sorted_transformed_value_array_gte[1]; + builder.sorted_transformed_value_array_gte[1] = tmp; + + builder.execute(); + } + + #[test(should_fail_with="value array must be sorted by counter")] + fn assert_split_sorted_transformed_value_array_asc_gte_wrong_sorted_order_fails() { + let mut builder = TestDataBuilder::new(); + + // Swap two values in the sorted array, also update their hints. + let tmp = builder.sorted_transformed_value_array_gte[0]; + builder.sorted_transformed_value_array_gte[0] = builder.sorted_transformed_value_array_gte[1]; + builder.sorted_transformed_value_array_gte[1] = tmp; + builder.hints.sorted_counters_gte[0] = 33; + builder.hints.sorted_counters_gte[1] = 22; + builder.update_sorted_index(33, 0); // Item of counter 33 is now at index 0 of the sorted array. + builder.update_sorted_index(22, 1); // Item of counter 22 is now at index 1 of the sorted array. + + builder.execute(); + } + + #[test(should_fail_with="mismatch counters")] + fn assert_split_sorted_transformed_value_array_asc_gte_wrong_counter_fails() { + let mut builder = TestDataBuilder::new(); + + // Swap two values in the sorted array, but keep their counters in the correct order. + let tmp = builder.sorted_transformed_value_array_gte[0]; + builder.sorted_transformed_value_array_gte[0] = builder.sorted_transformed_value_array_gte[1]; + builder.sorted_transformed_value_array_gte[1] = tmp; + builder.update_sorted_index(33, 0); // Item of counter 33 is now at index 0 of the sorted array. + builder.update_sorted_index(22, 1); // Item of counter 22 is now at index 1 of the sorted array. + + builder.execute(); + } + + #[test(should_fail_with="mismatch sorted values")] + fn assert_split_sorted_transformed_value_array_asc_misplace_lt_to_gte_fails() { + let mut builder = TestDataBuilder::new(); + + // Move the item with counter 44 from _gte to _lt. + builder.sorted_transformed_value_array_lt[2] = builder.sorted_transformed_value_array_gte[2]; + builder.hints.sorted_counters_lt[2] = 44; + builder.sorted_transformed_value_array_gte[2] = TestValue::empty(); + builder.hints.sorted_counters_lt[2] = 0; + builder.update_sorted_index(44, 2); // Item of counter 44 is now at index 2 of the sorted array. + + builder.execute(); + } + + #[test(should_fail_with="mismatch number of values lt")] + fn assert_split_sorted_transformed_value_array_asc_duplicate_lt_to_gte_fails() { + let mut builder = TestDataBuilder::new(); + + // Copy the item with counter 44 to _lt. + builder.sorted_transformed_value_array_lt[2] = builder.sorted_transformed_value_array_gte[2]; + builder.hints.sorted_counters_lt[2] = 44; + + builder.execute(); + } + + #[test(should_fail_with="mismatch number of values gte")] + fn assert_split_sorted_transformed_value_array_asc_gte_duplicate_items_fails() { + let mut builder = TestDataBuilder::new(); + + // Duplicate the item with counter 44. + builder.sorted_transformed_value_array_gte[3] = builder.sorted_transformed_value_array_gte[2]; + builder.hints.sorted_counters_gte[3] = 44; + + builder.execute(); + } + + #[test(should_fail_with="value array must be sorted by counter")] + fn assert_split_sorted_transformed_value_array_asc_multiple_zero_counter_items_fails() { + let mut builder = TestDataBuilder::new(); + + // Change the counter of the item from 11 to 0. + builder.original_array[2].counter = 0; + builder.hints.sorted_counters_lt[1] = 0; + + builder.execute(); + } + + #[test(should_fail_with="counters must not be the same")] + fn assert_split_sorted_transformed_value_array_desc_multiple_zero_counter_items_fails() { + let mut builder = TestDataBuilder::new_desc(); + + // Change the counter of the item from 11 to 0. + builder.original_array[2].counter = 0; + builder.hints.sorted_counters_lt[0] = 0; + + builder.execute(); + } + + #[test(should_fail_with="value array must be sorted by counter")] + fn assert_split_sorted_transformed_value_array_asc_lt_empty_item_at_index_0_fails() { + let mut builder = TestDataBuilder::new(); + + // Move the item at index i to index i + 1. + let num_items = 2; + for i in 0..num_items { + let from_index = num_items - 1 - i; + let to_index = num_items - i; + builder.sorted_transformed_value_array_lt[to_index] = builder.sorted_transformed_value_array_lt[from_index]; + let counter = builder.hints.sorted_counters_lt[from_index]; + builder.hints.sorted_counters_lt[to_index] = counter; + builder.update_sorted_index(counter, to_index); + } + // Empty the values at index 0. + builder.sorted_transformed_value_array_lt[0] = TestValue::empty(); + builder.hints.sorted_counters_lt[0] = 0; + + builder.execute(); + } + + #[test(should_fail_with="invalid array")] + fn assert_split_sorted_transformed_value_array_asc_gte_empty_item_at_index_0_fails() { + let mut builder = TestDataBuilder::new(); + + let num_items = 3; + for i in 0..num_items { + let from_index = num_items - 1 - i; + let to_index = num_items - i; + builder.sorted_transformed_value_array_gte[to_index] = builder.sorted_transformed_value_array_gte[from_index]; + let counter = builder.hints.sorted_counters_gte[from_index]; + builder.hints.sorted_counters_gte[to_index] = counter; + builder.update_sorted_index(counter, to_index); + } + // Empty the values at index 0. + builder.sorted_transformed_value_array_gte[0] = TestValue::empty(); + builder.hints.sorted_counters_gte[0] = 0; + + builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counters.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counters.nr index ecb342862b7..9c83a138f26 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counters.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_by_counters.nr @@ -8,14 +8,14 @@ pub fn order_by_counters_empty_padded_desc(a: T, b: T) -> bool where T: Order is_empty(b) | (!is_empty(a) & !is_empty(b) & a.counter() > b.counter()) } -pub fn sort_by_counters(array: [T; N], ordering: fn(T, T) -> bool) -> [T; N] { +fn sort_by(array: [T; N], ordering: fn(T, T) -> bool) -> [T; N] { array.sort_via(|a, b| ordering(a, b)) } pub fn sort_by_counters_asc(array: [T; N]) -> [T; N] where T: Ordered + Eq + Empty { - sort_by_counters(array, order_by_counters_empty_padded_asc) + sort_by(array, order_by_counters_empty_padded_asc) } pub fn sort_by_counters_desc(array: [T; N]) -> [T; N] where T: Ordered + Eq + Empty { - sort_by_counters(array, order_by_counters_empty_padded_desc) + sort_by(array, order_by_counters_empty_padded_desc) } 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 c8901d6722c..5163b3bc7f7 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 @@ -8,7 +8,7 @@ use crate::{ struct OrderHint { counter: u32, - sorted_index: u64, + sorted_index: u32, } impl OrderHint { @@ -23,7 +23,7 @@ impl Eq for OrderHint { } } -fn sort_get_order_hints( +pub fn sort_get_order_hints( array: [T; N], ordering: fn(T, T) -> bool ) -> [OrderHint; N] where T: Ordered + Eq + Empty { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_hints.nr new file mode 100644 index 00000000000..6fdde87227a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_hints.nr @@ -0,0 +1,62 @@ +use crate::{ + traits::{Empty, is_empty}, + utils::arrays::{sort_get_sorted_tuple::{sort_get_sorted_tuple, SortedTuple}} +}; + +struct SortedResult { + sorted_array: [T; N], + sorted_index_hints: [u32; N], +} + +pub fn sort_get_sorted_hints( + values: [T; N], + ordering: fn(T, T) -> bool +) -> SortedResult where T: Eq + Empty { + let sorted = sort_get_sorted_tuple( + values, + |a: T, b: T| is_empty(b) | (!is_empty(a) & !is_empty(b) & ordering(a, b)) + ); + + let sorted_array = sorted.map(|t: SortedTuple| t.elem); + let mut sorted_index_hints = [0; N]; + for i in 0..N { + if !is_empty(sorted[i].elem) { + let original_index = sorted[i].original_index; + sorted_index_hints[original_index] = i; + } + } + + SortedResult { sorted_array, sorted_index_hints } +} + +#[test] +fn sort_get_sorted_hints_asc_non_padded() { + let values = [40, 60, 20, 50]; + let res = sort_get_sorted_hints(values, |a: Field, b: Field| a.lt(b)); + assert_eq(res.sorted_array, [20, 40, 50, 60]); + assert_eq(res.sorted_index_hints, [1, 3, 0, 2]); +} + +#[test] +fn sort_get_sorted_hints_desc_non_padded() { + let values = [40, 20, 60, 50]; + let res = sort_get_sorted_hints(values, |a: Field, b: Field| b.lt(a)); + assert_eq(res.sorted_array, [60, 50, 40, 20]); + assert_eq(res.sorted_index_hints, [2, 3, 0, 1]); +} + +#[test] +fn sort_get_sorted_hints_asc_padded() { + let values = [40, 60, 20, 50, 0, 0]; + let res = sort_get_sorted_hints(values, |a: Field, b: Field| a.lt(b)); + assert_eq(res.sorted_array, [20, 40, 50, 60, 0, 0]); + assert_eq(res.sorted_index_hints, [1, 3, 0, 2, 0, 0]); +} + +#[test] +fn sort_get_sorted_hints_desc_padded() { + let values = [40, 20, 60, 50, 0, 0]; + let res = sort_get_sorted_hints(values, |a: Field, b: Field| b.lt(a)); + assert_eq(res.sorted_array, [60, 50, 40, 20, 0, 0]); + assert_eq(res.sorted_index_hints, [2, 3, 0, 1, 0, 0]); +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_tuple.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_tuple.nr index 4c2395332a8..848933a9763 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_tuple.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_sorted_tuple.nr @@ -3,7 +3,7 @@ struct SortedTuple { original_index: u64, } -pub fn sort_get_sorted_tuple(array: [T; N], ordering: fn(T, T) -> bool) -> [SortedTuple; N] { +pub fn sort_get_sorted_tuple(array: [T; N], ordering: fn[Env](T, T) -> bool) -> [SortedTuple; N] { let mut tuples = [SortedTuple { elem: array[0], original_index: 0 }; N]; for i in 0..N { tuples[i] = SortedTuple { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_split_order_hints.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_split_order_hints.nr new file mode 100644 index 00000000000..448e649d401 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/sort_get_split_order_hints.nr @@ -0,0 +1,279 @@ +use crate::{ + abis::side_effect::Ordered, traits::{Empty, is_empty}, + utils::arrays::{ + sort_by_counters::{order_by_counters_empty_padded_asc, order_by_counters_empty_padded_desc}, + sort_get_sorted_tuple::sort_get_sorted_tuple +} +}; + +struct SplitOrderHints { + sorted_counters_lt: [u32; N], + sorted_counters_gte: [u32; N], + sorted_indexes: [u32; N], +} + +impl SplitOrderHints { + pub fn empty() -> SplitOrderHints { + SplitOrderHints { sorted_counters_lt: [0; N], sorted_counters_gte: [0; N], sorted_indexes: [0; N] } + } +} + +impl Eq for SplitOrderHints { + fn eq(self, other: Self) -> bool { + (self.sorted_counters_lt == other.sorted_counters_lt) + & (self.sorted_counters_gte == other.sorted_counters_gte) + & (self.sorted_indexes == other.sorted_indexes) + } +} + +fn sort_get_split_order_hints( + array: [T; N], + split_counter: u32, + ascending: bool +) -> SplitOrderHints where T: Ordered + Eq + Empty { + let ordering = if ascending { + order_by_counters_empty_padded_asc + } else { + order_by_counters_empty_padded_desc + }; + let sorted_tuples = sort_get_sorted_tuple(array, ordering); + + let mut sorted_counters_lt = [0; N]; + let mut sorted_counters_gte = [0; N]; + let mut sorted_indexes = [0; N]; + let mut num_lt = 0; + let mut num_gte = 0; + let mut found_split = false; + for i in 0..N { + let elem = sorted_tuples[i].elem; + if !is_empty(elem) { + let is_gte = (elem.counter() >= split_counter) & (elem.counter() != 0); + found_split |= ascending == is_gte; + let mut index_offet = 0; + if found_split != ascending { + sorted_counters_lt[num_lt] = elem.counter(); + num_lt += 1; + index_offet = num_gte; + } else { + sorted_counters_gte[num_gte] = elem.counter(); + num_gte += 1; + index_offet = num_lt; + } + let original_index = sorted_tuples[i].original_index; + sorted_indexes[original_index] = if !found_split { i } else { i - index_offet }; + } + } + + SplitOrderHints { sorted_counters_lt, sorted_counters_gte, sorted_indexes } +} + +pub fn sort_get_split_order_hints_asc( + array: [T; N], + split_counter: u32 +) -> SplitOrderHints where T: Ordered + Eq + Empty { + sort_get_split_order_hints(array, split_counter, true) +} + +pub fn sort_get_split_order_hints_desc( + array: [T; N], + split_counter: u32 +) -> SplitOrderHints where T: Ordered + Eq + Empty { + sort_get_split_order_hints(array, split_counter, false) +} + +mod tests { + use crate::{ + abis::side_effect::Ordered, traits::Empty, + utils::arrays::sort_get_split_order_hints::{sort_get_split_order_hints_asc, sort_get_split_order_hints_desc, SplitOrderHints} + }; + + 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 full_array = [ + TestItem { value: 100, counter: 11 }, + TestItem { value: 200, counter: 17 }, + TestItem { value: 300, counter: 7 }, + TestItem { value: 400, counter: 5 }, + TestItem { value: 500, counter: 13 } + ]; + + global padded_array = [ + TestItem { value: 100, counter: 11 }, + TestItem { value: 200, counter: 17 }, + TestItem { value: 300, counter: 7 }, + TestItem { value: 400, counter: 5 }, + TestItem { value: 500, counter: 13 }, + TestItem::empty(), + TestItem::empty() + ]; + + // asc + + #[test] + fn sort_get_split_order_hints_asc_zero_split_counter_full() { + let split_counter = 0; + let hints = sort_get_split_order_hints_asc(full_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [0, 0, 0, 0, 0], + sorted_counters_gte: [5, 7, 11, 13, 17], + sorted_indexes: [2, 4, 1, 0, 3] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_asc_non_zero_split_counter_full() { + let split_counter = 9; + let hints = sort_get_split_order_hints_asc(full_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [5, 7, 0, 0, 0], + sorted_counters_gte: [11, 13, 17, 0, 0], + sorted_indexes: [0, 2, 1, 0, 1] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_asc_non_zero_split_counter_equal_full() { + let split_counter = 11; // Equal one of the item's counter. + let hints = sort_get_split_order_hints_asc(full_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [5, 7, 0, 0, 0], + sorted_counters_gte: [11, 13, 17, 0, 0], + sorted_indexes: [0, 2, 1, 0, 1] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_asc_zero_split_counter_padded_empty() { + let split_counter = 0; + let hints = sort_get_split_order_hints_asc(padded_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [0, 0, 0, 0, 0, 0, 0], + sorted_counters_gte: [5, 7, 11, 13, 17, 0, 0], + sorted_indexes: [2, 4, 1, 0, 3, 0, 0] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_asc_non_zero_split_counter_padded_empty() { + let split_counter = 9; + let hints = sort_get_split_order_hints_asc(padded_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [5, 7, 0, 0, 0, 0, 0], + sorted_counters_gte: [11, 13, 17, 0, 0, 0, 0], + sorted_indexes: [0, 2, 1, 0, 1, 0, 0] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_asc_non_zero_split_counter_equal_padded_empty() { + let split_counter = 11; + let hints = sort_get_split_order_hints_asc(padded_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [5, 7, 0, 0, 0, 0, 0], + sorted_counters_gte: [11, 13, 17, 0, 0, 0, 0], + sorted_indexes: [0, 2, 1, 0, 1, 0, 0] + }; + assert_eq(hints, expected_hints); + } + + // desc + + #[test] + fn sort_get_split_order_hints_desc_zero_split_counter_empty() { + let split_counter = 0; + let hints = sort_get_split_order_hints_desc(full_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [0, 0, 0, 0, 0], + sorted_counters_gte: [17, 13, 11, 7, 5], + sorted_indexes: [2, 0, 3, 4, 1] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_desc_non_zero_split_counter_empty() { + let split_counter = 9; + let hints = sort_get_split_order_hints_desc(full_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [7, 5, 0, 0, 0], + sorted_counters_gte: [17, 13, 11, 0, 0], + sorted_indexes: [2, 0, 0, 1, 1] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_desc_non_zero_split_counter_equal_empty() { + let split_counter = 11; + let hints = sort_get_split_order_hints_desc(full_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [7, 5, 0, 0, 0], + sorted_counters_gte: [17, 13, 11, 0, 0], + sorted_indexes: [2, 0, 0, 1, 1] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_desc_zero_split_counter_padded_empty() { + let split_counter = 0; + let hints = sort_get_split_order_hints_desc(padded_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [0, 0, 0, 0, 0, 0, 0], + sorted_counters_gte: [17, 13, 11, 7, 5, 0, 0], + sorted_indexes: [2, 0, 3, 4, 1, 0, 0] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_desc_non_zero_split_counter_padded_empty() { + let split_counter = 9; + let hints = sort_get_split_order_hints_desc(padded_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [7, 5, 0, 0, 0, 0, 0], + sorted_counters_gte: [17, 13, 11, 0, 0, 0, 0], + sorted_indexes: [2, 0, 0, 1, 1, 0, 0] + }; + assert_eq(hints, expected_hints); + } + + #[test] + fn sort_get_split_order_hints_desc_non_zero_split_counter_equal_padded_empty() { + let split_counter = 11; + let hints = sort_get_split_order_hints_desc(padded_array, split_counter); + let expected_hints = SplitOrderHints { + sorted_counters_lt: [7, 5, 0, 0, 0, 0, 0], + sorted_counters_gte: [17, 13, 11, 0, 0, 0, 0], + sorted_indexes: [2, 0, 0, 1, 1, 0, 0] + }; + assert_eq(hints, expected_hints); + } +} + diff --git a/noir-projects/noir-protocol-circuits/scripts/flamegraph.sh b/noir-projects/noir-protocol-circuits/scripts/flamegraph.sh new file mode 100755 index 00000000000..4a92d76b8eb --- /dev/null +++ b/noir-projects/noir-protocol-circuits/scripts/flamegraph.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +set -eu + +EXAMPLE_CMD="$0 private_kernel_init" + +# First arg is the circuit name. +if [[ $# -eq 0 || ($1 == -* && $1 != "-h") ]]; then + echo "Please specify the name of the circuit." + echo "e.g.: $EXAMPLE_CMD" + exit 1 +fi + +CIRCUIT_NAME=$1 +SERVE=false +PORT=5000 +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + echo "Generates a flamegraph for the specified protocol circuit." + echo "" + echo "Usage:" + echo " $0 " + echo "" + echo " e.g.: $EXAMPLE_CMD" + echo "" + echo "Arguments:" + echo " -s Serve the file over http" + echo " -p Specify custom port. Default: ${PORT}" + echo "" + exit 0 + ;; + -s|--serve) + SERVE=true + shift + ;; + -p|--port) + if [[ $# -lt 2 || $2 == -* ]]; then + echo "Please specify a port number." + echo "e.g.: $EXAMPLE_CMD -s -p 8080" + exit 1 + fi + PORT=$2 + shift 2 + ;; + *) + shift + ;; + esac +done + +# Get the directory of the script. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Check if the artifact exists. +ARTIFACT="$SCRIPT_DIR/../target/$CIRCUIT_NAME.json" +if [[ ! -f $ARTIFACT ]]; then + echo "Cannot find artifact: ${ARTIFACT}" + exit 1 +fi + +# Build profier if it's not available. +PROFILER="$SCRIPT_DIR/../../../noir/noir-repo/target/debug/noir-profiler" +if [ ! -f $PROFILER ]; then + echo "Profiler not found, building profiler" + cd "$SCRIPT_DIR/../../../noir/noir-repo/tooling/profiler" + cargo build + cd "$SCRIPT_DIR" +fi + +# We create dest directory and use it as an output for the generated main.svg file. +DEST="$SCRIPT_DIR/../dest" +mkdir -p $DEST + +# At last, generate the flamegraph. +$PROFILER gates-flamegraph --artifact-path "${ARTIFACT}" --backend-path "$SCRIPT_DIR/../../../barretenberg/cpp/build/bin/bb" --output "$DEST" + +# Serve the file over http if -s is set. +if $SERVE; then + echo "Serving flamegraph at http://0.0.0.0:${PORT}/main.svg" + python3 -m http.server --directory "$SCRIPT_DIR/../dest" $PORT +fi \ No newline at end of file diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts index b2b83ece100..57d1a88ddfe 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_init_circuit_private_inputs.ts @@ -5,10 +5,7 @@ import { TxRequest } from '../tx_request.js'; import { PrivateCallData } from './private_call_data.js'; export class PrivateKernelInitHints { - constructor( - public noteHashNullifierCounters: Tuple, - public firstRevertiblePrivateCallRequestIndex: number, - ) {} + constructor(public noteHashNullifierCounters: Tuple) {} toBuffer() { return serializeToBuffer(this.noteHashNullifierCounters); @@ -16,7 +13,7 @@ export class PrivateKernelInitHints { static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); - return new PrivateKernelInitHints(reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_CALL), reader.readNumber()); + return new PrivateKernelInitHints(reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_CALL)); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts index 151dbdc1e1c..d749d636a7b 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts @@ -1,113 +1,8 @@ -import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { - MAX_ENCRYPTED_LOGS_PER_TX, - MAX_NEW_NOTE_HASHES_PER_TX, - MAX_NEW_NULLIFIERS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, -} from '../../constants.gen.js'; import { countAccumulatedItems } from '../../utils/index.js'; -import { CallRequest } from '../call_request.js'; -import { NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash } from '../log_hash.js'; -import { ScopedNoteHash } from '../note_hash.js'; -import { ScopedNullifier } from '../nullifier.js'; import { PrivateKernelData } from './private_kernel_data.js'; -export class PrivateKernelTailHints { - constructor( - /* - * The sorted new note hashes. - */ - public sortedNewNoteHashes: Tuple, - /** - * The sorted new note hashes indexes. Maps original to sorted. - */ - public sortedNewNoteHashesIndexes: Tuple, - /** - * The sorted new nullifiers. Maps original to sorted. - */ - public sortedNewNullifiers: Tuple, - /** - * The sorted new nullifiers indexes. - */ - public sortedNewNullifiersIndexes: Tuple, - /** - * The sorted encrypted note log hashes. - */ - public sortedNoteEncryptedLogHashes: Tuple, - /** - * The sorted encrypted note log hashes indexes. Maps original to sorted. - */ - public sortedNoteEncryptedLogHashesIndexes: Tuple, - /** - * The sorted encrypted log hashes. - */ - public sortedEncryptedLogHashes: Tuple, - /** - * The sorted encrypted log hashes indexes. Maps original to sorted. - */ - public sortedEncryptedLogHashesIndexes: Tuple, - /** - * The sorted unencrypted log hashes. - */ - public sortedUnencryptedLogHashes: Tuple, - /** - * The sorted encrypted log hashes indexes. Maps original to sorted. - */ - public sortedUnencryptedLogHashesIndexes: Tuple, - /** - * The sorted public call requests. - */ - public sortedCallRequests: Tuple, - /** - * The sorted public call requests indexes. Maps original to sorted. - */ - public sortedCallRequestsIndexes: Tuple, - ) {} - - toBuffer() { - return serializeToBuffer( - this.sortedNewNoteHashes, - this.sortedNewNoteHashesIndexes, - this.sortedNewNullifiers, - this.sortedNewNullifiersIndexes, - this.sortedNoteEncryptedLogHashes, - this.sortedNoteEncryptedLogHashesIndexes, - this.sortedEncryptedLogHashes, - this.sortedEncryptedLogHashesIndexes, - this.sortedUnencryptedLogHashes, - this.sortedUnencryptedLogHashesIndexes, - this.sortedCallRequests, - this.sortedCallRequestsIndexes, - ); - } - - /** - * Deserializes from a buffer or reader. - * @param buffer - Buffer or reader to read from. - * @returns The deserialized instance. - */ - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new PrivateKernelTailHints( - reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, ScopedNoteHash), - reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), - reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, ScopedNullifier), - reader.readNumbers(MAX_NEW_NULLIFIERS_PER_TX), - reader.readArray(MAX_NOTE_ENCRYPTED_LOGS_PER_TX, NoteLogHash), - reader.readNumbers(MAX_NOTE_ENCRYPTED_LOGS_PER_TX), - reader.readArray(MAX_ENCRYPTED_LOGS_PER_TX, ScopedEncryptedLogHash), - reader.readNumbers(MAX_ENCRYPTED_LOGS_PER_TX), - reader.readArray(MAX_UNENCRYPTED_LOGS_PER_TX, ScopedLogHash), - reader.readNumbers(MAX_UNENCRYPTED_LOGS_PER_TX), - reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), - reader.readNumbers(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX), - ); - } -} - /** * Input to the private kernel circuit - tail call. */ @@ -117,7 +12,6 @@ export class PrivateKernelTailCircuitPrivateInputs { * The previous kernel data */ public previousKernel: PrivateKernelData, - public hints: PrivateKernelTailHints, ) {} isForPublic() { @@ -129,7 +23,7 @@ export class PrivateKernelTailCircuitPrivateInputs { * @returns The buffer. */ toBuffer() { - return serializeToBuffer(this.previousKernel, this.hints); + return serializeToBuffer(this.previousKernel); } /** @@ -139,9 +33,6 @@ export class PrivateKernelTailCircuitPrivateInputs { */ static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelTailCircuitPrivateInputs { const reader = BufferReader.asReader(buffer); - return new PrivateKernelTailCircuitPrivateInputs( - reader.readObject(PrivateKernelData), - reader.readObject(PrivateKernelTailHints), - ); + return new PrivateKernelTailCircuitPrivateInputs(reader.readObject(PrivateKernelData)); } } diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 24629f1e8f7..7f7f3e6fa98 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -15,6 +15,8 @@ import { toBufferLE } from '@aztec/foundation/bigint-buffer'; import { sha256 } from '@aztec/foundation/crypto'; import { CardGameContract } from '@aztec/noir-contracts.js/CardGame'; +import { jest } from '@jest/globals'; + import { setup } from './fixtures/utils.js'; /* eslint-disable camelcase */ @@ -59,7 +61,11 @@ const GAME_ID = 42; const PLAYER_SECRET_KEYS = INITIAL_TEST_SECRET_KEYS; +const TIMEOUT = 600_000; + describe('e2e_card_game', () => { + jest.setTimeout(TIMEOUT); + let pxe: PXE; let logger: DebugLogger; let teardown: () => Promise; diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 28448f316c1..3cfd493bbe8 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -89,7 +89,6 @@ import { type PrivateKernelResetOutputs, type PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailCircuitPublicInputs, - type PrivateKernelTailHints, PublicAccumulatedData, type PublicCallData, type PublicCallStackItem, @@ -205,9 +204,7 @@ import type { PrivateKernelResetHints as PrivateKernelResetHintsNoir, PrivateKernelResetOutputs as PrivateKernelResetOutputsNoir, PrivateKernelTailCircuitPrivateInputs as PrivateKernelTailCircuitPrivateInputsNoir, - PrivateKernelTailHints as PrivateKernelTailHintsNoir, PrivateKernelTailToPublicCircuitPrivateInputs as PrivateKernelTailToPublicCircuitPrivateInputsNoir, - PrivateKernelTailToPublicHints as PrivateKernelTailToPublicHintsNoir, PublicAccumulatedData as PublicAccumulatedDataNoir, PublicCallData as PublicCallDataNoir, PublicCallStackItem as PublicCallStackItemNoir, @@ -1602,7 +1599,6 @@ export function mapPrivateKernelTailCircuitPublicInputsForPublicFromNoir( function mapPrivateKernelInitHintsToNoir(inputs: PrivateKernelInitHints): PrivateKernelInitHintsNoir { return { note_hash_nullifier_counters: mapTuple(inputs.noteHashNullifierCounters, mapNumberToNoir), - first_revertible_private_call_request_index: mapNumberToNoir(inputs.firstRevertiblePrivateCallRequestIndex), }; } @@ -1640,38 +1636,6 @@ function mapPrivateKernelResetOutputsToNoir(inputs: PrivateKernelResetOutputs): }; } -function mapPrivateKernelTailHintsToNoir(inputs: PrivateKernelTailHints): PrivateKernelTailHintsNoir { - return { - sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapScopedNoteHashToNoir), - sorted_new_note_hashes_indexes: mapTuple(inputs.sortedNewNoteHashesIndexes, mapNumberToNoir), - sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapScopedNullifierToNoir), - sorted_new_nullifiers_indexes: mapTuple(inputs.sortedNewNullifiersIndexes, mapNumberToNoir), - sorted_note_encrypted_log_hashes: mapTuple(inputs.sortedNoteEncryptedLogHashes, mapNoteLogHashToNoir), - sorted_note_encrypted_log_hashes_indexes: mapTuple(inputs.sortedNoteEncryptedLogHashesIndexes, mapNumberToNoir), - sorted_encrypted_log_hashes: mapTuple(inputs.sortedEncryptedLogHashes, mapScopedEncryptedLogHashToNoir), - sorted_encrypted_log_hashes_indexes: mapTuple(inputs.sortedEncryptedLogHashesIndexes, mapNumberToNoir), - sorted_unencrypted_log_hashes: mapTuple(inputs.sortedUnencryptedLogHashes, mapScopedLogHashToNoir), - sorted_unencrypted_log_hashes_indexes: mapTuple(inputs.sortedUnencryptedLogHashesIndexes, mapNumberToNoir), - }; -} - -function mapPrivateKernelTailToPublicHintsToNoir(inputs: PrivateKernelTailHints): PrivateKernelTailToPublicHintsNoir { - return { - sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapScopedNoteHashToNoir), - sorted_new_note_hashes_indexes: mapTuple(inputs.sortedNewNoteHashesIndexes, mapNumberToNoir), - sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapScopedNullifierToNoir), - sorted_new_nullifiers_indexes: mapTuple(inputs.sortedNewNullifiersIndexes, mapNumberToNoir), - sorted_note_encrypted_log_hashes: mapTuple(inputs.sortedNoteEncryptedLogHashes, mapNoteLogHashToNoir), - sorted_note_encrypted_log_hashes_indexes: mapTuple(inputs.sortedNoteEncryptedLogHashesIndexes, mapNumberToNoir), - sorted_encrypted_log_hashes: mapTuple(inputs.sortedEncryptedLogHashes, mapScopedEncryptedLogHashToNoir), - sorted_encrypted_log_hashes_indexes: mapTuple(inputs.sortedEncryptedLogHashesIndexes, mapNumberToNoir), - sorted_unencrypted_log_hashes: mapTuple(inputs.sortedUnencryptedLogHashes, mapScopedLogHashToNoir), - sorted_unencrypted_log_hashes_indexes: mapTuple(inputs.sortedUnencryptedLogHashesIndexes, mapNumberToNoir), - sorted_call_requests: mapTuple(inputs.sortedCallRequests, mapCallRequestToNoir), - sorted_call_requests_indexes: mapTuple(inputs.sortedCallRequestsIndexes, mapNumberToNoir), - }; -} - function mapPrivateKernelResetHintsToNoir< NH_RR_PENDING extends number, NH_RR_SETTLED extends number, @@ -1738,7 +1702,6 @@ export function mapPrivateKernelTailCircuitPrivateInputsToNoir( ): PrivateKernelTailCircuitPrivateInputsNoir { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - hints: mapPrivateKernelTailHintsToNoir(inputs.hints), }; } @@ -1747,7 +1710,6 @@ export function mapPrivateKernelTailToPublicCircuitPrivateInputsToNoir( ): PrivateKernelTailToPublicCircuitPrivateInputsNoir { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - hints: mapPrivateKernelTailToPublicHintsToNoir(inputs.hints), }; } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index 068d2ba12f8..33d62677e6c 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -35,7 +35,6 @@ import { buildPrivateKernelInitHints, buildPrivateKernelInnerHints, buildPrivateKernelResetInputs, - buildPrivateKernelTailHints, } from './private_inputs_builders/index.js'; import { type ProvingDataOracle } from './proving_data_oracle.js'; @@ -111,7 +110,6 @@ export class KernelProver { const hints = buildPrivateKernelInitHints( currentExecution.callStackItem.publicInputs, noteHashNullifierCounterMap, - currentExecution.callStackItem.publicInputs.privateCallRequests, ); const proofInput = new PrivateKernelInitCircuitPrivateInputs(txRequest, privateCallData, hints); pushTestData('private-kernel-inputs-init', proofInput); @@ -152,9 +150,7 @@ export class KernelProver { `Calling private kernel tail with hwm ${previousKernelData.publicInputs.minRevertibleSideEffectCounter}`, ); - const hints = buildPrivateKernelTailHints(output.publicInputs); - - const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData, hints); + const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData); pushTestData('private-kernel-inputs-ordering', privateInputs); return await this.proofCreator.createProofTail(privateInputs); diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_init_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_init_hints.ts index b81c952d023..bd5e305cede 100644 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_init_hints.ts +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_init_hints.ts @@ -1,28 +1,17 @@ import { type MAX_NEW_NOTE_HASHES_PER_CALL, - type PrivateCallRequest, type PrivateCircuitPublicInputs, PrivateKernelInitHints, - countAccumulatedItems, } from '@aztec/circuits.js'; import { type Tuple } from '@aztec/foundation/serialize'; export function buildPrivateKernelInitHints( publicInputs: PrivateCircuitPublicInputs, noteHashNullifierCounterMap: Map, - privateCallRequests: PrivateCallRequest[], ) { const nullifierCounters = publicInputs.newNoteHashes.map( n => noteHashNullifierCounterMap.get(n.counter) ?? 0, ) as Tuple; - const minRevertibleCounter = publicInputs.minRevertibleSideEffectCounter.toNumber(); - let firstRevertiblePrivateCallRequestIndex = privateCallRequests.findIndex( - r => r.startSideEffectCounter >= minRevertibleCounter, - ); - if (firstRevertiblePrivateCallRequestIndex === -1) { - firstRevertiblePrivateCallRequestIndex = countAccumulatedItems(privateCallRequests); - } - - return new PrivateKernelInitHints(nullifierCounters, firstRevertiblePrivateCallRequestIndex); + return new PrivateKernelInitHints(nullifierCounters); } diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts deleted file mode 100644 index 09a11570dac..00000000000 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - MAX_ENCRYPTED_LOGS_PER_TX, - MAX_NEW_NOTE_HASHES_PER_TX, - MAX_NEW_NULLIFIERS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, - type PrivateKernelCircuitPublicInputs, - PrivateKernelTailHints, - sortByCounterGetSortedHints, -} from '@aztec/circuits.js'; - -export function buildPrivateKernelTailHints(publicInputs: PrivateKernelCircuitPublicInputs): PrivateKernelTailHints { - const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints( - publicInputs.end.newNoteHashes, - MAX_NEW_NOTE_HASHES_PER_TX, - ); - - const [sortedNullifiers, sortedNullifiersIndexes] = sortByCounterGetSortedHints( - publicInputs.end.newNullifiers, - MAX_NEW_NULLIFIERS_PER_TX, - ); - - const [sortedNoteEncryptedLogHashes, sortedNoteEncryptedLogHashesIndexes] = sortByCounterGetSortedHints( - publicInputs.end.noteEncryptedLogsHashes, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX, - ); - - const [sortedEncryptedLogHashes, sortedEncryptedLogHashesIndexes] = sortByCounterGetSortedHints( - publicInputs.end.encryptedLogsHashes, - MAX_ENCRYPTED_LOGS_PER_TX, - ); - - const [sortedUnencryptedLogHashes, sortedUnencryptedLogHashesIndexes] = sortByCounterGetSortedHints( - publicInputs.end.unencryptedLogsHashes, - MAX_UNENCRYPTED_LOGS_PER_TX, - ); - - const [sortedCallRequests, sortedCallRequestsIndexes] = sortByCounterGetSortedHints( - publicInputs.end.publicCallStack, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - { - ascending: false, - hintIndexesBy: 'sorted', - }, - ); - - return new PrivateKernelTailHints( - sortedNoteHashes, - sortedNoteHashesIndexes, - sortedNullifiers, - sortedNullifiersIndexes, - sortedNoteEncryptedLogHashes, - sortedNoteEncryptedLogHashesIndexes, - sortedEncryptedLogHashes, - sortedEncryptedLogHashesIndexes, - sortedUnencryptedLogHashes, - sortedUnencryptedLogHashesIndexes, - sortedCallRequests, - sortedCallRequestsIndexes, - ); -} diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts index 07dee2ce561..56b9765bf9c 100644 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/index.ts @@ -1,5 +1,4 @@ export { buildPrivateKernelInitHints } from './build_private_kernel_init_hints.js'; export { buildPrivateKernelInnerHints } from './build_private_kernel_inner_hints.js'; -export { buildPrivateKernelTailHints } from './build_private_kernel_tail_hints.js'; export { buildPrivateKernelResetInputs } from './build_private_kernel_reset_hints.js'; export { buildPrivateKernelResetOutputs } from './build_private_kernel_reset_outputs.js';