diff --git a/noir-projects/noir-protocol-circuits/Nargo.toml b/noir-projects/noir-protocol-circuits/Nargo.toml index 48cc2fc96f6..b57cec006f1 100644 --- a/noir-projects/noir-protocol-circuits/Nargo.toml +++ b/noir-projects/noir-protocol-circuits/Nargo.toml @@ -9,6 +9,8 @@ members = [ "crates/private-kernel-init-simulated", "crates/private-kernel-inner", "crates/private-kernel-inner-simulated", + "crates/private-kernel-reset", + "crates/private-kernel-reset-simulated", "crates/private-kernel-tail", "crates/private-kernel-tail-simulated", "crates/private-kernel-tail-to-public", diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr index 18fb6cce881..770cf2d7101 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr @@ -1,4 +1,3 @@ -use dep::reset_kernel_lib::verify_squashed_transient_note_hashes_and_nullifiers; use dep::types::{ abis::{ private_kernel_data::PrivateKernelData, @@ -23,12 +22,7 @@ fn asc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { struct KernelCircuitPublicInputsComposer { public_inputs: PrivateKernelCircuitPublicInputsBuilder, previous_kernel: PrivateKernelData, - // Final data - note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], // Hints - transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], sorted_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], sorted_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], @@ -42,10 +36,6 @@ struct KernelCircuitPublicInputsComposer { impl KernelCircuitPublicInputsComposer { pub fn new( previous_kernel: PrivateKernelData, - note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], - transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], sorted_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], sorted_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], sorted_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], @@ -60,10 +50,6 @@ impl KernelCircuitPublicInputsComposer { KernelCircuitPublicInputsComposer { public_inputs, previous_kernel, - note_hashes, - nullifiers, - transient_nullifier_indexes_for_note_hashes, - transient_note_hash_indexes_for_nullifiers, sorted_note_hashes, sorted_note_hashes_indexes, sorted_nullifiers, @@ -79,6 +65,7 @@ impl KernelCircuitPublicInputsComposer { assert_eq( array_length(self.previous_kernel.public_inputs.end.private_call_stack), 0, "Private call stack must be empty when executing the tail circuit" ); + self.verify_empty_validation_requests(); self.propagate_rollup_validation_requests(); @@ -88,8 +75,7 @@ impl KernelCircuitPublicInputsComposer { self.propagate_fee_payer(); - // TODO: Should be done in a reset circuit. - self.squash_transient_data(); + self.verify_no_transient_data(); self.silo_values(); @@ -236,26 +222,28 @@ impl KernelCircuitPublicInputsComposer { self.public_inputs.fee_payer = self.previous_kernel.public_inputs.fee_payer; } - fn squash_transient_data(&mut self) { - verify_squashed_transient_note_hashes_and_nullifiers( - self.public_inputs.end.new_note_hashes.storage, - self.public_inputs.end.new_nullifiers.storage, - self.note_hashes, - self.nullifiers, - self.transient_nullifier_indexes_for_note_hashes, - self.transient_note_hash_indexes_for_nullifiers - ); - - // Currently all the transient note hashes and nullifiers must be cleared in the tail circuits. + fn verify_no_transient_data(self) { + // Currently all the transient note hashes and nullifiers must be cleared in the reset circuits. // Check that the propagated note hashes don't link to a nullifier, and vice versa. - for i in 0..self.note_hashes.len() { - assert(self.note_hashes[i].nullifier_counter == 0, "Unresolved transient note hash"); + for note_hash in self.public_inputs.end.new_note_hashes.storage { + assert(note_hash.nullifier_counter == 0, "Unresolved transient note hash"); } - for i in 0..self.nullifiers.len() { - assert(self.nullifiers[i].nullified_note_hash() == 0, "Unresolved transient nullifier"); + for new_nullifier in self.public_inputs.end.new_nullifiers.storage { + assert(new_nullifier.nullified_note_hash() == 0, "Unresolved transient nullifier"); } + } + + fn verify_empty_validation_requests(self) { + assert_eq( + array_length(self.previous_kernel.public_inputs.validation_requests.note_hash_read_requests), 0, "Non empty note hash read requests" + ); - self.public_inputs.end.new_note_hashes = array_to_bounded_vec(self.note_hashes); - self.public_inputs.end.new_nullifiers = array_to_bounded_vec(self.nullifiers); + assert_eq( + array_length(self.previous_kernel.public_inputs.validation_requests.nullifier_read_requests), 0, "Non empty nullifier read requests" + ); + + assert_eq( + array_length(self.previous_kernel.public_inputs.validation_requests.nullifier_key_validation_requests), 0, "Non empty nullifier key validation requests" + ); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr index 88de299ebac..8ba81e439c4 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/lib.nr @@ -6,8 +6,10 @@ mod private_kernel_inner; mod private_kernel_tail; mod private_kernel_tail_to_public; mod tests; +mod private_kernel_reset; use private_kernel_init::PrivateKernelInitCircuitPrivateInputs; use private_kernel_inner::PrivateKernelInnerCircuitPrivateInputs; +use private_kernel_reset::PrivateKernelResetCircuitPrivateInputs; use private_kernel_tail::PrivateKernelTailCircuitPrivateInputs; use private_kernel_tail_to_public::PrivateKernelTailToPublicCircuitPrivateInputs; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr new file mode 100644 index 00000000000..69110229d27 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -0,0 +1,427 @@ +use crate::private_kernel_circuit_public_inputs_composer::PrivateKernelCircuitPublicInputsComposer; +use dep::reset_kernel_lib::{ + NoteHashReadRequestHints, NullifierReadRequestHints, PrivateValidationRequestProcessor, + verify_squashed_transient_note_hashes_and_nullifiers +}; +use dep::types::{ + abis::{ + private_kernel_data::{PrivateKernelData, verify_previous_kernel_proof}, + kernel_circuit_public_inputs::KernelCircuitPublicInputs, note_hash::ScopedNoteHash, + nullifier::ScopedNullifier, side_effect::SideEffect +}, + constants::{ + MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX +}, + grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty, + PrivateKernelCircuitPublicInputs +}; + +// Can just be KernelCircuitPublicInputs. +struct PrivateKernelResetOutputs { + note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], + nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], +} + +struct PrivateKernelResetHints { + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints: NoteHashReadRequestHints, + nullifier_read_request_hints: NullifierReadRequestHints, + master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], +} + +struct PrivateKernelResetCircuitPrivateInputs { + previous_kernel: PrivateKernelData, + outputs: PrivateKernelResetOutputs, + hints: PrivateKernelResetHints, +} + +impl PrivateKernelResetCircuitPrivateInputs { + pub fn execute(self) -> PrivateKernelCircuitPublicInputs { + let mut previous_public_inputs = self.previous_kernel.public_inputs; + + // verify/aggregate the previous kernel + verify_previous_kernel_proof(self.previous_kernel); + + previous_public_inputs.validation_requests = PrivateValidationRequestProcessor { + validation_requests: previous_public_inputs.validation_requests, + note_hash_read_request_hints: self.hints.note_hash_read_request_hints, + pending_note_hashes: previous_public_inputs.end.new_note_hashes, + note_hash_tree_root: previous_public_inputs.constants.historical_header.state.partial.note_hash_tree.root, + nullifier_read_request_hints: self.hints.nullifier_read_request_hints, + pending_nullifiers: previous_public_inputs.end.new_nullifiers, + nullifier_tree_root: previous_public_inputs.constants.historical_header.state.partial.nullifier_tree.root, + master_nullifier_secret_keys: self.hints.master_nullifier_secret_keys + }.validate(); + + verify_squashed_transient_note_hashes_and_nullifiers( + previous_public_inputs.end.new_note_hashes, + previous_public_inputs.end.new_nullifiers, + self.outputs.note_hashes, + self.outputs.nullifiers, + self.hints.transient_nullifier_indexes_for_note_hashes, + self.hints.transient_note_hash_indexes_for_nullifiers + ); + + previous_public_inputs.end.new_note_hashes = self.outputs.note_hashes; + previous_public_inputs.end.new_nullifiers = self.outputs.nullifiers; + + previous_public_inputs + } +} + +mod tests { + use crate::private_kernel_reset::{PrivateKernelResetCircuitPrivateInputs, PrivateKernelResetHints, PrivateKernelResetOutputs}; + 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} + }, + 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_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD + }; + use dep::types::{ + abis::{ + kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, + max_block_number::MaxBlockNumber, note_hash::{NoteHash, ScopedNoteHash}, + nullifier::{Nullifier, ScopedNullifier}, side_effect::SideEffect, + read_request::ScopedReadRequest + }, + address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, + hash::{compute_note_hash_nonce, compute_unique_note_hash, sha256_to_field, silo_note_hash, silo_nullifier}, + tests::{fixture_builder::FixtureBuilder}, utils::{arrays::{array_eq, array_length}}, + traits::{Empty, is_empty, is_empty_array}, grumpkin_point::GrumpkinPoint + }; + + struct PrivateKernelResetInputsBuilder { + previous_kernel: FixtureBuilder, + transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints_builder: NoteHashReadRequestHintsBuilder, + nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder, + } + + impl PrivateKernelResetInputsBuilder { + pub fn new() -> Self { + let mut previous_kernel = FixtureBuilder::new(); + previous_kernel.append_new_nullifiers(1); + + PrivateKernelResetInputsBuilder { + previous_kernel, + transient_nullifier_indexes_for_note_hashes: [MAX_NEW_NULLIFIERS_PER_TX; MAX_NEW_NOTE_HASHES_PER_TX], + transient_note_hash_indexes_for_nullifiers: [MAX_NEW_NOTE_HASHES_PER_TX; MAX_NEW_NULLIFIERS_PER_TX], + note_hash_read_request_hints_builder: NoteHashReadRequestHintsBuilder::new(MAX_NOTE_HASH_READ_REQUESTS_PER_TX), + nullifier_read_request_hints_builder: NullifierReadRequestHintsBuilder::new(MAX_NULLIFIER_READ_REQUESTS_PER_TX) + } + } + + pub fn add_pending_note_hash_read_request(&mut self, note_hash_index: u64) { + let read_request_index = self.previous_kernel.add_read_request_for_pending_note_hash(note_hash_index); + let hint_index = self.note_hash_read_request_hints_builder.pending_read_hints.len(); + let hint = PendingReadHint { read_request_index, pending_value_index: note_hash_index }; + self.note_hash_read_request_hints_builder.pending_read_hints.push(hint); + self.note_hash_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; + } + + pub fn add_pending_nullifier_read_request(&mut self, nullifier_index_offset_one: u64) { + let nullifier_index = nullifier_index_offset_one + 1; // + 1 is for the first nullifier + let read_request_index = self.previous_kernel.add_read_request_for_pending_nullifier(nullifier_index); + let hint_index = self.nullifier_read_request_hints_builder.pending_read_hints.len(); + let hint = PendingReadHint { read_request_index, pending_value_index: nullifier_index }; + self.nullifier_read_request_hints_builder.pending_read_hints.push(hint); + self.nullifier_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; + } + + pub fn nullify_pending_note_hash(&mut self, nullifier_index: u64, note_hash_index: u64) { + self.previous_kernel.new_note_hashes.storage[note_hash_index].nullifier_counter = self.previous_kernel.new_nullifiers.get(nullifier_index).counter(); + self.previous_kernel.new_nullifiers.storage[nullifier_index].nullifier.note_hash = self.previous_kernel.new_note_hashes.get(note_hash_index).note_hash.value; + self.transient_nullifier_indexes_for_note_hashes[note_hash_index] = nullifier_index; + self.transient_note_hash_indexes_for_nullifiers[nullifier_index] = note_hash_index; + } + + pub fn execute(&mut self) -> PrivateKernelCircuitPublicInputs { + let outputs = PrivateKernelResetOutputs { + note_hashes: squash_transient_note_hashes(self.previous_kernel.new_note_hashes.storage), + nullifiers: squash_transient_nullifiers(self.previous_kernel.new_nullifiers.storage) + }; + + let hints = PrivateKernelResetHints { + transient_nullifier_indexes_for_note_hashes: self.transient_nullifier_indexes_for_note_hashes, + transient_note_hash_indexes_for_nullifiers: self.transient_note_hash_indexes_for_nullifiers, + note_hash_read_request_hints: self.note_hash_read_request_hints_builder.to_hints(), + nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), + master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX] + }; + + let kernel = PrivateKernelResetCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), outputs, hints }; + kernel.execute() + } + + pub fn failed(&mut self) { + let _ = self.execute(); + } + + pub fn succeeded(&mut self) { + let _ = self.execute(); + } + } + + #[test] + unconstrained fn execution_succeeded() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + let _public_inputs = builder.execute(); + } + + #[test] + unconstrained fn propagate_previous_kernel_max_block_number() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + builder.previous_kernel.max_block_number = MaxBlockNumber::new(13); + let public_inputs = builder.execute(); + + assert_eq(public_inputs.validation_requests.for_rollup.max_block_number.unwrap(), 13); + } + + #[test] + unconstrained fn two_pending_note_hash_read_request() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_note_hashes(3); + builder.add_pending_note_hash_read_request(1); + builder.add_pending_note_hash_read_request(0); + + builder.succeeded(); + } + + #[test(should_fail_with="Value of the note hash does not match read request")] + unconstrained fn pending_note_hash_read_request_wrong_hint_fails() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_note_hashes(3); + builder.add_pending_note_hash_read_request(1); + let mut hint = builder.note_hash_read_request_hints_builder.pending_read_hints.pop(); + hint.pending_value_index = 2; + builder.note_hash_read_request_hints_builder.pending_read_hints.push(hint); + + builder.failed(); + } + + #[test] + unconstrained fn one_pending_nullifier_read_request() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_nullifiers(3); + builder.add_pending_nullifier_read_request(1); + + builder.succeeded(); + } + + #[test] + unconstrained fn two_pending_nullifier_read_requests() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_nullifiers(3); + builder.add_pending_nullifier_read_request(1); + builder.add_pending_nullifier_read_request(0); + + builder.succeeded(); + } + + #[test(should_fail_with="Value of the nullifier does not match read request")] + unconstrained fn pending_nullifier_read_request_wrong_hint_fails() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_nullifiers(3); + builder.add_pending_nullifier_read_request(1); + let mut hint = builder.nullifier_read_request_hints_builder.pending_read_hints.pop(); + assert(hint.pending_value_index == 2); + hint.pending_value_index = 1; + builder.nullifier_read_request_hints_builder.pending_read_hints.push(hint); + + builder.failed(); + } + + #[test(should_fail_with="Read request counter must be greater than the counter of the nullifier")] + unconstrained fn pending_nullifier_read_request_reads_before_value_fails() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_nullifiers(3); + builder.add_pending_nullifier_read_request(1); + let nullifier_being_read = builder.previous_kernel.new_nullifiers.storage[2]; + let mut read_request = builder.previous_kernel.nullifier_read_requests.pop(); + read_request.read_request.counter = nullifier_being_read.counter() - 1; + builder.previous_kernel.nullifier_read_requests.push(read_request); + + builder.failed(); + } + + #[test] + unconstrained fn propagates_unverified_requests() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_note_hashes(3); + builder.previous_kernel.append_new_nullifiers(3); + + builder.add_pending_note_hash_read_request(0); + builder.add_pending_nullifier_read_request(1); + + // Now add some read requests that will be propagated + let remaining_note_hash_rr_index = builder.previous_kernel.add_read_request_for_pending_note_hash(1); + let note_hash_rr = builder.previous_kernel.note_hash_read_requests.storage[remaining_note_hash_rr_index]; + + let remaining_nullifier_rr_index = builder.previous_kernel.add_read_request_for_pending_nullifier(1); + let nullifier_rr = builder.previous_kernel.nullifier_read_requests.storage[remaining_nullifier_rr_index]; + + let nk_validation_index = builder.previous_kernel.add_request_for_nullifier_key_validation(GrumpkinPoint::new(1, 2), 27); + let nk_validation = builder.previous_kernel.nullifier_key_validation_requests.storage[nk_validation_index]; + + // Check that they have been propagated to the next kernel + let result = builder.execute(); + + assert_eq(result.validation_requests.note_hash_read_requests[0], note_hash_rr); + assert_eq(result.validation_requests.nullifier_read_requests[0], nullifier_rr); + assert_eq(result.validation_requests.nullifier_key_validation_requests[0], nk_validation); + } + + #[test] + unconstrained fn native_squash_one_of_one_transient_matches_works() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(1); + builder.previous_kernel.append_new_nullifiers(2); + // The nullifier at index 1 is nullifying the hash at index 0; + builder.nullify_pending_note_hash(1, 0); + let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; + let public_inputs = builder.execute(); + + assert(is_empty_array(public_inputs.end.new_note_hashes)); + + // The nullifier at index 1 is chopped. + assert( + array_eq( + public_inputs.end.new_nullifiers, + [new_nullifiers[0], new_nullifiers[2]] + ) + ); + } + + #[test] + unconstrained fn native_squash_one_of_two_transient_matches_works() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(2); + builder.previous_kernel.append_new_nullifiers(2); + // The nullifier at index 1 is nullifying the hash at index 0; + builder.nullify_pending_note_hash(1, 0); + let new_note_hashes = builder.previous_kernel.new_note_hashes.storage; + let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; + let public_inputs = builder.execute(); + + // The 0th hash is chopped. + assert(array_eq(public_inputs.end.new_note_hashes, [new_note_hashes[1]])); + + // The nullifier at index 1 is chopped. + assert( + array_eq( + public_inputs.end.new_nullifiers, + [new_nullifiers[0], new_nullifiers[2]] + ) + ); + } + + #[test] + unconstrained fn native_squash_two_of_two_transient_matches_works() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(2); + builder.previous_kernel.append_new_nullifiers(2); + // The nullifier at index 1 is nullifying the hash at index 1; + builder.nullify_pending_note_hash(1, 1); + // The nullifier at index 2 is nullifying the hash at index 0; + builder.nullify_pending_note_hash(2, 0); + let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; + let public_inputs = builder.execute(); + + assert(is_empty_array(public_inputs.end.new_note_hashes)); + + // Only the first nullifier is left after squashing. + assert(array_eq(public_inputs.end.new_nullifiers, [new_nullifiers[0]])); + } + + #[test] + unconstrained fn squash_unordered_transient_notes_works() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + + builder.previous_kernel.append_new_note_hashes(3); + // Shuffle the note hashes so they will have to be re-ordered. + let tmp = builder.previous_kernel.new_note_hashes.storage[0]; + builder.previous_kernel.new_note_hashes.storage[0] = builder.previous_kernel.new_note_hashes.storage[1]; + builder.previous_kernel.new_note_hashes.storage[1] = builder.previous_kernel.new_note_hashes.storage[2]; + builder.previous_kernel.new_note_hashes.storage[2] = tmp; + + builder.previous_kernel.append_new_nullifiers(3); + // Shuffle the nullifers so they will have to be re-ordered. + let tmp = builder.previous_kernel.new_nullifiers.storage[1]; + builder.previous_kernel.new_nullifiers.storage[1] = builder.previous_kernel.new_nullifiers.storage[3]; + builder.previous_kernel.new_nullifiers.storage[3] = builder.previous_kernel.new_nullifiers.storage[2]; + builder.previous_kernel.new_nullifiers.storage[2] = tmp; + + // The nullifier at index 1 is nullifying the note hash at index 1; + builder.nullify_pending_note_hash(1, 1); + // The nullifier at index 2 is nullifying the note hash at index 2; + builder.nullify_pending_note_hash(2, 2); + // The nullifier at index 3 is nullifying the note hash at index 0; + builder.nullify_pending_note_hash(3, 0); + + let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; + let public_inputs = builder.execute(); + + assert(is_empty_array(public_inputs.end.new_note_hashes)); + + // Only the first nullifier is left after squashing. + assert(array_eq(public_inputs.end.new_nullifiers, [new_nullifiers[0]])); + } + + #[test(should_fail_with="Value of the hinted transient note hash does not match")] + unconstrained fn wrong_transient_nullifier_index_for_note_hash_fails() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(1); + builder.previous_kernel.append_new_nullifiers(1); + // The nullifier at index 1 is nullifying the hash at index 0; + builder.nullify_pending_note_hash(1, 0); + // Change the hint to be out of bounds. + builder.transient_nullifier_indexes_for_note_hashes[0] = 0; + builder.failed(); + } + + #[test(should_fail_with="Invalid transient nullifier index hint")] + unconstrained fn wrong_transient_nullifier_index_hint_fails() { + let mut builder = PrivateKernelResetInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(2); + builder.previous_kernel.append_new_nullifiers(2); + // The nullifier at index 1 is nullifying the hash at index 1; + builder.nullify_pending_note_hash(1, 1); + // The nullifier at index 2 is nullifying the hash at index 0; + builder.nullify_pending_note_hash(2, 0); + // Tweak the hint to be for the hash at index 1. + builder.transient_note_hash_indexes_for_nullifiers[2] = 1; + builder.failed(); + } + + #[test] + unconstrained fn propagate_fee_payer() { + // Check that we carry forward if the fee payer is already set + let mut builder = PrivateKernelResetInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.fee_payer = fee_payer; + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer remains empty if unset + let mut builder = PrivateKernelResetInputsBuilder::new(); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } +} 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 b04669d7e36..f8470fa2433 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,5 +1,4 @@ use crate::kernel_circuit_public_inputs_composer::KernelCircuitPublicInputsComposer; -use dep::reset_kernel_lib::{NoteHashReadRequestHints, NullifierReadRequestHints, PrivateValidationRequestProcessor}; use dep::types::{ abis::{ private_kernel_data::{PrivateKernelData, verify_previous_kernel_proof}, @@ -13,18 +12,7 @@ use dep::types::{ grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty }; -// Can just be KernelCircuitPublicInputs. -struct PrivateKernelTailOutputs { - note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], -} - struct PrivateKernelTailHints { - transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], - note_hash_read_request_hints: NoteHashReadRequestHints, - nullifier_read_request_hints: NullifierReadRequestHints, - master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], sorted_new_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], sorted_new_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], sorted_new_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], @@ -37,12 +25,11 @@ struct PrivateKernelTailHints { struct PrivateKernelTailCircuitPrivateInputs { previous_kernel: PrivateKernelData, - outputs: PrivateKernelTailOutputs, hints: PrivateKernelTailHints, } impl PrivateKernelTailCircuitPrivateInputs { - pub fn native_private_kernel_circuit_tail(self) -> KernelCircuitPublicInputs { + pub fn execute(self) -> KernelCircuitPublicInputs { let previous_public_inputs = self.previous_kernel.public_inputs; assert_eq( array_length(previous_public_inputs.end.public_call_stack), 0, "Public call stack must be empty when executing the tail circuit" @@ -54,25 +41,8 @@ impl PrivateKernelTailCircuitPrivateInputs { // verify/aggregate the previous kernel verify_previous_kernel_proof(self.previous_kernel); - let note_hash_tree_root = previous_public_inputs.constants.historical_header.state.partial.note_hash_tree.root; - let nullifier_tree_root = previous_public_inputs.constants.historical_header.state.partial.nullifier_tree.root; - PrivateValidationRequestProcessor { - validation_requests: previous_public_inputs.validation_requests, - note_hash_read_request_hints: self.hints.note_hash_read_request_hints, - pending_note_hashes: previous_public_inputs.end.new_note_hashes, - note_hash_tree_root, - nullifier_read_request_hints: self.hints.nullifier_read_request_hints, - pending_nullifiers: previous_public_inputs.end.new_nullifiers, - nullifier_tree_root, - master_nullifier_secret_keys: self.hints.master_nullifier_secret_keys - }.validate(); - KernelCircuitPublicInputsComposer::new( self.previous_kernel, - self.outputs.note_hashes, - self.outputs.nullifiers, - self.hints.transient_nullifier_indexes_for_note_hashes, - self.hints.transient_note_hash_indexes_for_nullifiers, self.hints.sorted_new_note_hashes, self.hints.sorted_new_note_hashes_indexes, self.hints.sorted_new_nullifiers, @@ -86,7 +56,7 @@ impl PrivateKernelTailCircuitPrivateInputs { } mod tests { - use crate::private_kernel_tail::{PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailHints, PrivateKernelTailOutputs}; + use crate::private_kernel_tail::{PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailHints}; use dep::reset_kernel_lib::{ tests::{ note_hash_read_request_hints_builder::NoteHashReadRequestHintsBuilder, @@ -109,7 +79,8 @@ mod tests { address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, hash::{compute_note_hash_nonce, compute_unique_note_hash, sha256_to_field, silo_note_hash, silo_nullifier}, tests::{fixture_builder::FixtureBuilder, sort::sort_get_sorted_hints}, - utils::{arrays::{array_eq, array_length}}, traits::{Empty, is_empty, is_empty_array} + utils::{arrays::{array_eq, array_length}}, traits::{Empty, is_empty, is_empty_array}, + grumpkin_point::GrumpkinPoint }; // TODO: Reduce the duplicated code/tests for PrivateKernelTailInputs and PrivateKernelTailToPublicInputs. @@ -162,30 +133,6 @@ mod tests { output } - pub fn add_pending_note_hash_read_request(&mut self, note_hash_index: u64) { - let read_request_index = self.previous_kernel.add_read_request_for_pending_note_hash(note_hash_index); - let hint_index = self.note_hash_read_request_hints_builder.pending_read_hints.len(); - let hint = PendingReadHint { read_request_index, pending_value_index: note_hash_index }; - self.note_hash_read_request_hints_builder.pending_read_hints.push(hint); - self.note_hash_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; - } - - pub fn add_pending_nullifier_read_request(&mut self, nullifier_index_offset_one: u64) { - let nullifier_index = nullifier_index_offset_one + 1; // + 1 is for the first nullifier - let read_request_index = self.previous_kernel.add_read_request_for_pending_nullifier(nullifier_index); - let hint_index = self.nullifier_read_request_hints_builder.pending_read_hints.len(); - let hint = PendingReadHint { read_request_index, pending_value_index: nullifier_index }; - self.nullifier_read_request_hints_builder.pending_read_hints.push(hint); - self.nullifier_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; - } - - pub fn nullify_pending_note_hash(&mut self, nullifier_index: u64, note_hash_index: u64) { - self.previous_kernel.new_note_hashes.storage[note_hash_index].nullifier_counter = self.previous_kernel.new_nullifiers.get(nullifier_index).counter(); - self.previous_kernel.new_nullifiers.storage[nullifier_index].nullifier.note_hash = self.previous_kernel.new_note_hashes.get(note_hash_index).note_hash.value; - self.transient_nullifier_indexes_for_note_hashes[note_hash_index] = nullifier_index; - self.transient_note_hash_indexes_for_nullifiers[nullifier_index] = note_hash_index; - } - pub fn execute(&mut self) -> KernelCircuitPublicInputs { let sorted = sort_get_sorted_hints( self.previous_kernel.new_note_hashes.storage, @@ -233,17 +180,7 @@ mod tests { } } - let outputs = PrivateKernelTailOutputs { - note_hashes: squash_transient_note_hashes(sorted_new_note_hashes), - nullifiers: squash_transient_nullifiers(sorted_new_nullifiers) - }; - let hints = PrivateKernelTailHints { - transient_nullifier_indexes_for_note_hashes: sorted_transient_nullifier_indexes_for_note_hashes, - transient_note_hash_indexes_for_nullifiers: sorted_transient_note_hash_indexes_for_nullifiers, - note_hash_read_request_hints: self.note_hash_read_request_hints_builder.to_hints(), - nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), - master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], sorted_new_note_hashes, sorted_new_note_hashes_indexes, sorted_new_nullifiers, @@ -254,8 +191,8 @@ mod tests { sorted_unencrypted_log_hashes_indexes }; - let kernel = PrivateKernelTailCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), outputs, hints }; - kernel.native_private_kernel_circuit_tail() + let kernel = PrivateKernelTailCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), hints }; + kernel.execute() } pub fn failed(&mut self) { @@ -327,170 +264,6 @@ mod tests { assert_eq(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); } - #[test] - unconstrained fn two_pending_note_hash_read_request() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_note_hashes(3); - builder.add_pending_note_hash_read_request(1); - builder.add_pending_note_hash_read_request(0); - - builder.succeeded(); - } - - #[test(should_fail_with="Value of the note hash does not match read request")] - unconstrained fn pending_note_hash_read_request_wrong_hint_fails() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_note_hashes(3); - builder.add_pending_note_hash_read_request(1); - let mut hint = builder.note_hash_read_request_hints_builder.pending_read_hints.pop(); - hint.pending_value_index = 2; - builder.note_hash_read_request_hints_builder.pending_read_hints.push(hint); - - builder.failed(); - } - - #[test] - unconstrained fn one_pending_nullifier_read_request() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - - builder.succeeded(); - } - - #[test] - unconstrained fn two_pending_nullifier_read_requests() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - builder.add_pending_nullifier_read_request(0); - - builder.succeeded(); - } - - #[test(should_fail_with="Value of the nullifier does not match read request")] - unconstrained fn pending_nullifier_read_request_wrong_hint_fails() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - let mut hint = builder.nullifier_read_request_hints_builder.pending_read_hints.pop(); - assert(hint.pending_value_index == 2); - hint.pending_value_index = 1; - builder.nullifier_read_request_hints_builder.pending_read_hints.push(hint); - - builder.failed(); - } - - #[test(should_fail_with="Read request counter must be greater than the counter of the nullifier")] - unconstrained fn pending_nullifier_read_request_reads_before_value_fails() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - let nullifier_being_read = builder.previous_kernel.new_nullifiers.storage[2]; - let mut read_request = builder.previous_kernel.nullifier_read_requests.pop(); - read_request.read_request.counter = nullifier_being_read.counter() - 1; - builder.previous_kernel.nullifier_read_requests.push(read_request); - - builder.failed(); - } - - #[test] - unconstrained fn native_squash_one_of_one_transient_matches_works() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(1); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(1, 0); - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - assert(is_empty_array(public_inputs.end.new_note_hashes)); - - // The nullifier at index 1 is chopped. - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0], new_nullifiers[2]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - - #[test] - unconstrained fn native_squash_one_of_two_transient_matches_works() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(2); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(1, 0); - let new_note_hashes = builder.previous_kernel.new_note_hashes.storage; - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - // The 0th hash is chopped. - let expected_note_hashes = builder.compute_output_note_hashes([new_note_hashes[1]]); - assert(array_eq(public_inputs.end.new_note_hashes, expected_note_hashes)); - - // The nullifier at index 1 is chopped. - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0], new_nullifiers[2]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - - #[test] - unconstrained fn native_squash_two_of_two_transient_matches_works() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(2); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 1; - builder.nullify_pending_note_hash(1, 1); - // The nullifier at index 2 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(2, 0); - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - assert(is_empty_array(public_inputs.end.new_note_hashes)); - - // Only the first nullifier is left after squashing. - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - - #[test] - unconstrained fn squash_unordered_transient_notes_works() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - - builder.previous_kernel.append_new_note_hashes(3); - // Shuffle the note hashes so they will have to be re-ordered. - let tmp = builder.previous_kernel.new_note_hashes.storage[0]; - builder.previous_kernel.new_note_hashes.storage[0] = builder.previous_kernel.new_note_hashes.storage[1]; - builder.previous_kernel.new_note_hashes.storage[1] = builder.previous_kernel.new_note_hashes.storage[2]; - builder.previous_kernel.new_note_hashes.storage[2] = tmp; - - builder.previous_kernel.append_new_nullifiers(3); - // Shuffle the nullifers so they will have to be re-ordered. - let tmp = builder.previous_kernel.new_nullifiers.storage[1]; - builder.previous_kernel.new_nullifiers.storage[1] = builder.previous_kernel.new_nullifiers.storage[3]; - builder.previous_kernel.new_nullifiers.storage[3] = builder.previous_kernel.new_nullifiers.storage[2]; - builder.previous_kernel.new_nullifiers.storage[2] = tmp; - - // The nullifier at index 1 is nullifying the note hash at index 1; - builder.nullify_pending_note_hash(1, 1); - // The nullifier at index 2 is nullifying the note hash at index 2; - builder.nullify_pending_note_hash(2, 2); - // The nullifier at index 3 is nullifying the note hash at index 0; - builder.nullify_pending_note_hash(3, 0); - - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - assert(is_empty_array(public_inputs.end.new_note_hashes)); - - // Only the first nullifier is left after squashing. - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - #[test] unconstrained fn ordering_of_note_hashes_and_nullifiers() { let mut builder = PrivateKernelTailInputsBuilder::new(); @@ -534,32 +307,6 @@ mod tests { assert_eq(public_inputs.end.gas_used, expected_gas); } - #[test(should_fail_with="Value of the hinted transient note hash does not match")] - unconstrained fn wrong_transient_nullifier_index_for_note_hash_fails() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(1); - builder.previous_kernel.append_new_nullifiers(1); - // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(1, 0); - // Change the hint to be out of bounds. - builder.transient_nullifier_indexes_for_note_hashes[0] = 0; - builder.failed(); - } - - #[test(should_fail_with="Invalid transient nullifier index hint")] - unconstrained fn wrong_transient_nullifier_index_hint_fails() { - let mut builder = PrivateKernelTailInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(2); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 1; - builder.nullify_pending_note_hash(1, 1); - // The nullifier at index 2 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(2, 0); - // Tweak the hint to be for the hash at index 1. - builder.transient_note_hash_indexes_for_nullifiers[2] = 1; - builder.failed(); - } - #[test(should_fail_with="Private call stack must be empty when executing the tail circuit")] unconstrained fn non_empty_private_call_stack_should_fail() { let mut builder = PrivateKernelTailInputsBuilder::new(); @@ -581,6 +328,29 @@ mod tests { builder.failed(); } + #[test(should_fail_with="Non empty note hash read requests")] + unconstrained fn non_empty_note_hash_read_requests() { + let mut builder = PrivateKernelTailInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(3); + builder.previous_kernel.add_read_request_for_pending_note_hash(1); + builder.failed(); + } + + #[test(should_fail_with="Non empty nullifier read requests")] + unconstrained fn non_empty_nullifier_read_requests() { + let mut builder = PrivateKernelTailInputsBuilder::new(); + builder.previous_kernel.append_new_nullifiers(3); + builder.previous_kernel.add_read_request_for_pending_nullifier(1); + builder.failed(); + } + + #[test(should_fail_with="Non empty nullifier key validation requests")] + unconstrained fn non_empty_nullifier_key_validations() { + let mut builder = PrivateKernelTailInputsBuilder::new(); + builder.previous_kernel.add_request_for_nullifier_key_validation(GrumpkinPoint::new(1, 2), 27); + builder.failed(); + } + #[test] unconstrained fn empty_tx_consumes_teardown_limits_plus_fixed_gas() { let mut builder = PrivateKernelTailInputsBuilder::new(); 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 27490b7e12b..9809e1d2fff 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,5 +1,4 @@ use crate::kernel_circuit_public_inputs_composer::KernelCircuitPublicInputsComposer; -use dep::reset_kernel_lib::{NoteHashReadRequestHints, NullifierReadRequestHints, PrivateValidationRequestProcessor}; use dep::types::{ abis::{ private_kernel_data::{PrivateKernelData, verify_previous_kernel_proof}, @@ -13,18 +12,7 @@ use dep::types::{ grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty }; -// Can just be PublicKernelCircuitPublicInputs. -struct PrivateKernelTailToPublicOutputs { - note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], - nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], -} - struct PrivateKernelTailToPublicHints { - transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], - transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], - note_hash_read_request_hints: NoteHashReadRequestHints, - nullifier_read_request_hints: NullifierReadRequestHints, - master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], sorted_new_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], sorted_new_note_hashes_indexes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], sorted_new_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], @@ -37,8 +25,7 @@ struct PrivateKernelTailToPublicHints { struct PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: PrivateKernelData, - outputs: PrivateKernelTailToPublicOutputs, - hints: PrivateKernelTailToPublicHints + hints: PrivateKernelTailToPublicHints, } impl PrivateKernelTailToPublicCircuitPrivateInputs { @@ -53,25 +40,8 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { // verify/aggregate the previous kernel verify_previous_kernel_proof(self.previous_kernel); - let note_hash_tree_root = previous_public_inputs.constants.historical_header.state.partial.note_hash_tree.root; - let nullifier_tree_root = previous_public_inputs.constants.historical_header.state.partial.nullifier_tree.root; - PrivateValidationRequestProcessor { - validation_requests: previous_public_inputs.validation_requests, - note_hash_read_request_hints: self.hints.note_hash_read_request_hints, - pending_note_hashes: previous_public_inputs.end.new_note_hashes, - note_hash_tree_root, - nullifier_read_request_hints: self.hints.nullifier_read_request_hints, - pending_nullifiers: previous_public_inputs.end.new_nullifiers, - nullifier_tree_root, - master_nullifier_secret_keys: self.hints.master_nullifier_secret_keys - }.validate(); - KernelCircuitPublicInputsComposer::new( self.previous_kernel, - self.outputs.note_hashes, - self.outputs.nullifiers, - self.hints.transient_nullifier_indexes_for_note_hashes, - self.hints.transient_note_hash_indexes_for_nullifiers, self.hints.sorted_new_note_hashes, self.hints.sorted_new_note_hashes_indexes, self.hints.sorted_new_nullifiers, @@ -85,10 +55,7 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { } mod tests { - use crate::private_kernel_tail_to_public::{ - PrivateKernelTailToPublicCircuitPrivateInputs, PrivateKernelTailToPublicHints, - PrivateKernelTailToPublicOutputs - }; + use crate::private_kernel_tail_to_public::{PrivateKernelTailToPublicCircuitPrivateInputs, PrivateKernelTailToPublicHints}; use dep::reset_kernel_lib::{ tests::{ note_hash_read_request_hints_builder::NoteHashReadRequestHintsBuilder, @@ -111,7 +78,7 @@ mod tests { address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, hash::{compute_note_hash_nonce, compute_unique_note_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 + utils::{arrays::{array_eq, array_length}}, traits::is_empty_array, grumpkin_point::GrumpkinPoint }; // TODO: Reduce the duplicated code/tests for PrivateKernelTailToPublicInputs and PrivateKernelTailInputs. @@ -170,30 +137,6 @@ mod tests { output } - pub fn add_pending_note_hash_read_request(&mut self, note_hash_index: u64) { - let read_request_index = self.previous_kernel.add_read_request_for_pending_note_hash(note_hash_index); - let hint_index = self.note_hash_read_request_hints_builder.pending_read_hints.len(); - let hint = PendingReadHint { read_request_index, pending_value_index: note_hash_index }; - self.note_hash_read_request_hints_builder.pending_read_hints.push(hint); - self.note_hash_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; - } - - pub fn add_pending_nullifier_read_request(&mut self, nullifier_index_offset_one: u64) { - let nullifier_index = nullifier_index_offset_one + 1; // + 1 is for the first nullifier - let read_request_index = self.previous_kernel.add_read_request_for_pending_nullifier(nullifier_index); - let hint_index = self.nullifier_read_request_hints_builder.pending_read_hints.len(); - let hint = PendingReadHint { read_request_index, pending_value_index: nullifier_index }; - self.nullifier_read_request_hints_builder.pending_read_hints.push(hint); - self.nullifier_read_request_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; - } - - pub fn nullify_pending_note_hash(&mut self, nullifier_index: u64, note_hash_index: u64) { - self.previous_kernel.new_note_hashes.storage[note_hash_index].nullifier_counter = self.previous_kernel.new_nullifiers.get(nullifier_index).counter(); - self.previous_kernel.new_nullifiers.storage[nullifier_index].nullifier.note_hash = self.previous_kernel.new_note_hashes.get(note_hash_index).value(); - self.transient_nullifier_indexes_for_note_hashes[note_hash_index] = nullifier_index; - self.transient_note_hash_indexes_for_nullifiers[nullifier_index] = note_hash_index; - } - pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { let sorted = sort_get_sorted_hints( self.previous_kernel.new_note_hashes.storage, @@ -223,35 +166,7 @@ mod tests { let sorted_unencrypted_log_hashes = sorted.sorted_array; let sorted_unencrypted_log_hashes_indexes = sorted.sorted_index_hints; - let mut sorted_transient_nullifier_indexes_for_note_hashes = [MAX_NEW_NULLIFIERS_PER_TX; MAX_NEW_NOTE_HASHES_PER_TX]; - for i in 0..self.transient_nullifier_indexes_for_note_hashes.len() { - let old_index = self.transient_nullifier_indexes_for_note_hashes[i]; - if old_index != MAX_NEW_NULLIFIERS_PER_TX { - let new_note_hash_index = sorted_new_note_hashes_indexes[i]; - sorted_transient_nullifier_indexes_for_note_hashes[new_note_hash_index] = sorted_new_nullifiers_indexes[old_index]; - } - } - - let mut sorted_transient_note_hash_indexes_for_nullifiers = [MAX_NEW_NOTE_HASHES_PER_TX; MAX_NEW_NULLIFIERS_PER_TX]; - for i in 0..self.transient_note_hash_indexes_for_nullifiers.len() { - let old_index = self.transient_note_hash_indexes_for_nullifiers[i]; - if old_index != MAX_NEW_NOTE_HASHES_PER_TX { - let new_nullifier_index = sorted_new_nullifiers_indexes[i]; - sorted_transient_note_hash_indexes_for_nullifiers[new_nullifier_index] = sorted_new_note_hashes_indexes[old_index]; - } - } - - let outputs = PrivateKernelTailToPublicOutputs { - note_hashes: squash_transient_note_hashes(sorted_new_note_hashes), - nullifiers: squash_transient_nullifiers(sorted_new_nullifiers) - }; - let hints = PrivateKernelTailToPublicHints { - transient_nullifier_indexes_for_note_hashes: sorted_transient_nullifier_indexes_for_note_hashes, - transient_note_hash_indexes_for_nullifiers: sorted_transient_note_hash_indexes_for_nullifiers, - note_hash_read_request_hints: self.note_hash_read_request_hints_builder.to_hints(), - nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), - master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], sorted_new_note_hashes, sorted_new_note_hashes_indexes, sorted_new_nullifiers, @@ -262,7 +177,7 @@ mod tests { sorted_unencrypted_log_hashes_indexes }; - let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), outputs, hints }; + let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), hints }; kernel.execute() } @@ -275,134 +190,6 @@ mod tests { } } - #[test] - unconstrained fn two_pending_note_hash_read_request() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_new_note_hashes(3); - builder.add_pending_note_hash_read_request(1); - builder.add_pending_note_hash_read_request(0); - - builder.succeeded(); - } - - #[test(should_fail_with="Value of the note hash does not match read request")] - unconstrained fn pending_note_hash_read_request_wrong_hint_fails() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_new_note_hashes(3); - builder.add_pending_note_hash_read_request(1); - let mut hint = builder.note_hash_read_request_hints_builder.pending_read_hints.pop(); - hint.pending_value_index = 2; - builder.note_hash_read_request_hints_builder.pending_read_hints.push(hint); - - builder.failed(); - } - - #[test] - unconstrained fn one_pending_nullifier_read_request() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - - builder.succeeded(); - } - - #[test] - unconstrained fn two_pending_nullifier_read_requests() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - builder.add_pending_nullifier_read_request(0); - - builder.succeeded(); - } - - #[test(should_fail_with="Value of the nullifier does not match read request")] - unconstrained fn pending_nullifier_read_request_wrong_hint_fails() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - let mut hint = builder.nullifier_read_request_hints_builder.pending_read_hints.pop(); - assert(hint.pending_value_index == 2); - hint.pending_value_index = 1; - builder.nullifier_read_request_hints_builder.pending_read_hints.push(hint); - - builder.failed(); - } - - #[test(should_fail_with="Read request counter must be greater than the counter of the nullifier")] - unconstrained fn pending_nullifier_read_request_reads_before_value_fails() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - - builder.previous_kernel.append_new_nullifiers(3); - builder.add_pending_nullifier_read_request(1); - let nullifier_being_read = builder.previous_kernel.new_nullifiers.storage[2]; - let mut read_request = builder.previous_kernel.nullifier_read_requests.pop(); - read_request.read_request.counter = nullifier_being_read.counter() - 1; - builder.previous_kernel.nullifier_read_requests.push(read_request); - - builder.failed(); - } - - #[test] - unconstrained fn native_squash_one_of_one_transient_matches_works() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(1); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(1, 0); - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - assert(is_empty_array(public_inputs.end.new_note_hashes)); - - // The nullifier at index 1 is chopped. - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0], new_nullifiers[2]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - - #[test] - unconstrained fn native_squash_one_of_two_transient_matches_works() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(2); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(1, 0); - let new_note_hashes = builder.previous_kernel.new_note_hashes.storage; - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - // The 0th hash will be chopped. - let expected_note_hashes = builder.compute_output_note_hashes([new_note_hashes[1]]); - assert(array_eq(public_inputs.end.new_note_hashes, expected_note_hashes)); - - // The nullifier at index 1 is chopped. - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0], new_nullifiers[2]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - - #[test] - unconstrained fn native_squash_two_of_two_transient_matches_works() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(2); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 1; - builder.nullify_pending_note_hash(1, 1); - // The nullifier at index 2 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(2, 0); - let new_nullifiers = builder.previous_kernel.new_nullifiers.storage; - let public_inputs = builder.execute(); - - // Only the first nullifier is left after squashing. - assert(is_empty_array(public_inputs.end.new_note_hashes)); - let expected_nullifiers = builder.compute_output_nullifiers([new_nullifiers[0]]); - assert(array_eq(public_inputs.end.new_nullifiers, expected_nullifiers)); - } - #[test] unconstrained fn ordering_of_note_hashes_and_nullifiers() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); @@ -434,32 +221,6 @@ mod tests { } } - #[test(should_fail_with="Value of the hinted transient note hash does not match")] - unconstrained fn wrong_transient_nullifier_index_for_note_hash_fails() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(1); - builder.previous_kernel.append_new_nullifiers(1); - // The nullifier at index 1 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(1, 0); - // Change the hint to be out of bounds. - builder.transient_nullifier_indexes_for_note_hashes[0] = 0; - builder.failed(); - } - - #[test(should_fail_with="Invalid transient nullifier index hint")] - unconstrained fn wrong_transient_nullifier_index_hint_fails() { - let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - builder.previous_kernel.append_new_note_hashes(2); - builder.previous_kernel.append_new_nullifiers(2); - // The nullifier at index 1 is nullifying the hash at index 1; - builder.nullify_pending_note_hash(1, 1); - // The nullifier at index 2 is nullifying the hash at index 0; - builder.nullify_pending_note_hash(2, 0); - // Tweak the hint to be for the hash at index 1. - builder.transient_note_hash_indexes_for_nullifiers[2] = 1; - builder.failed(); - } - #[test(should_fail_with="Private call stack must be empty when executing the tail circuit")] unconstrained fn non_empty_private_call_stack_should_fail() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); @@ -549,24 +310,27 @@ mod tests { ); } - #[test] - unconstrained fn split_side_effect_squashing() { + #[test(should_fail_with="Non empty note hash read requests")] + unconstrained fn non_empty_note_hash_read_requests() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + builder.previous_kernel.append_new_note_hashes(3); + builder.previous_kernel.add_read_request_for_pending_note_hash(1); + builder.failed(); + } - // add one hash in non-revertible part - builder.previous_kernel.append_new_note_hashes(1); - builder.previous_kernel.end_setup(); - - // nullify it in revertible part - builder.previous_kernel.append_new_nullifiers(1); - builder.nullify_pending_note_hash(1, 0); - - let public_inputs = builder.execute(); + #[test(should_fail_with="Non empty nullifier read requests")] + unconstrained fn non_empty_nullifier_read_requests() { + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + builder.previous_kernel.append_new_nullifiers(3); + builder.previous_kernel.add_read_request_for_pending_nullifier(1); + builder.failed(); + } - assert(!is_empty_array(public_inputs.end_non_revertible.new_nullifiers)); - assert(is_empty_array(public_inputs.end_non_revertible.new_note_hashes)); - assert(is_empty_array(public_inputs.end.new_note_hashes)); - assert(is_empty_array(public_inputs.end.new_nullifiers)); + #[test(should_fail_with="Non empty nullifier key validation requests")] + unconstrained fn non_empty_nullifier_key_validations() { + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + builder.previous_kernel.add_request_for_nullifier_key_validation(GrumpkinPoint::new(1, 2), 27); + builder.failed(); } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/Nargo.toml new file mode 100644 index 00000000000..d9dd4b853dc --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "private_kernel_reset_simulated" +type = "bin" +authors = [""] +compiler_version = ">=0.18.0" + +[dependencies] +private_kernel_lib = { path = "../private-kernel-lib" } +types = { path = "../types" } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr new file mode 100644 index 00000000000..36b736b9ea7 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr @@ -0,0 +1,6 @@ +use dep::private_kernel_lib::PrivateKernelResetCircuitPrivateInputs; +use dep::types::PrivateKernelCircuitPublicInputs; + +unconstrained fn main(input: PrivateKernelResetCircuitPrivateInputs) -> pub PrivateKernelCircuitPublicInputs { + input.execute() +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/Nargo.toml new file mode 100644 index 00000000000..4ebdaf02f59 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "private_kernel_reset" +type = "bin" +authors = [""] +compiler_version = ">=0.18.0" + +[dependencies] +private_kernel_lib = { path = "../private-kernel-lib" } +types = { path = "../types" } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr new file mode 100644 index 00000000000..fddb8407810 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr @@ -0,0 +1,7 @@ +use dep::private_kernel_lib::PrivateKernelResetCircuitPrivateInputs; +use dep::types::PrivateKernelCircuitPublicInputs; + +#[recursive] +fn main(input: PrivateKernelResetCircuitPrivateInputs) -> pub PrivateKernelCircuitPublicInputs { + input.execute() +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-tail-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-tail-simulated/src/main.nr index bedd642aeaa..5eebea5eef6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-tail-simulated/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-tail-simulated/src/main.nr @@ -2,5 +2,5 @@ use dep::private_kernel_lib::PrivateKernelTailCircuitPrivateInputs; use dep::types::KernelCircuitPublicInputs; unconstrained fn main(input: PrivateKernelTailCircuitPrivateInputs) -> pub KernelCircuitPublicInputs { - input.native_private_kernel_circuit_tail() + input.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-tail/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-tail/src/main.nr index 41485a79a2b..e20050715d7 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-tail/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-tail/src/main.nr @@ -3,5 +3,5 @@ use dep::types::KernelCircuitPublicInputs; #[recursive] fn main(input: PrivateKernelTailCircuitPrivateInputs) -> pub KernelCircuitPublicInputs { - input.native_private_kernel_circuit_tail() + input.execute() } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr index 08d63cb14d8..d8516c91ae2 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr @@ -3,12 +3,17 @@ use crate::{ nullifier_read_request_reset::NullifierReadRequestHints, reset::read_request::reset_read_requests }; use dep::types::{ - abis::{note_hash::ScopedNoteHash, nullifier::ScopedNullifier, validation_requests::ValidationRequests}, + abis::{ + note_hash::ScopedNoteHash, nullifier::ScopedNullifier, validation_requests::ValidationRequests, + read_request::ScopedReadRequest, + nullifier_key_validation_request::ScopedNullifierKeyValidationRequest +}, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, - GENERATOR_INDEX__NSK_M + GENERATOR_INDEX__NSK_M, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX }, - grumpkin_private_key::GrumpkinPrivateKey, hash::poseidon2_hash, traits::is_empty + grumpkin_private_key::GrumpkinPrivateKey, hash::poseidon2_hash, traits::is_empty, + utils::arrays::filter_array_to_bounded_vec }; struct PrivateValidationRequestProcessor { @@ -23,66 +28,74 @@ struct PrivateValidationRequestProcessor { } impl PrivateValidationRequestProcessor { - pub fn validate(self) { - self.validate_note_hash_read_requests(); - self.validate_nullifier_read_requests(); - self.validate_nullifier_keys(); + pub fn validate(self) -> ValidationRequests { + let remaining_note_hash_read_requests = self.validate_note_hash_read_requests(); + let remaining_nullifier_read_requests = self.validate_nullifier_read_requests(); + let remaining_nullifier_key_validation_requests = self.validate_nullifier_keys(); + + ValidationRequests { + for_rollup: self.validation_requests.for_rollup, + nullifier_non_existent_read_requests: self.validation_requests.nullifier_non_existent_read_requests, + public_data_reads: self.validation_requests.public_data_reads, + note_hash_read_requests: remaining_note_hash_read_requests.storage, + nullifier_read_requests: remaining_nullifier_read_requests.storage, + nullifier_key_validation_requests: remaining_nullifier_key_validation_requests.storage + } } - fn validate_note_hash_read_requests(self) { - let remaining_requests = reset_read_requests( + fn validate_note_hash_read_requests(self) -> BoundedVec { + reset_read_requests( self.validation_requests.note_hash_read_requests, self.pending_note_hashes, self.note_hash_read_request_hints.read_request_statuses, self.note_hash_read_request_hints.pending_read_hints, self.note_hash_read_request_hints.settled_read_hints, self.note_hash_tree_root - ); - // When we have a separate reset circuit, we can allow unverified requests and process them later after the - // corresponding values are added to public inputs in nested executions. - // But right now, all the request must be cleared in one go. - assert(remaining_requests.len() == 0, "All note hash read requests must be verified"); + ) } - fn validate_nullifier_read_requests(self) { - let remaining_requests = reset_read_requests( + fn validate_nullifier_read_requests(self) -> BoundedVec { + reset_read_requests( self.validation_requests.nullifier_read_requests, self.pending_nullifiers, self.nullifier_read_request_hints.read_request_statuses, self.nullifier_read_request_hints.pending_read_hints, self.nullifier_read_request_hints.settled_read_hints, self.nullifier_tree_root - ); - // When we have a separate reset circuit, we can allow unverified requests and process them later after the - // corresponding values are added to public inputs in nested executions. - // But right now, all the request must be cleared in one go. - assert(remaining_requests.len() == 0, "All nullifier read requests must be verified"); + ) } - fn validate_nullifier_keys(self) { + fn validate_nullifier_keys(self) -> BoundedVec { + let mut should_propagate = [false; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX]; let requests = self.validation_requests.nullifier_key_validation_requests; for i in 0..MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX { let request = requests[i].request; if !is_empty(request) { let contract_address = requests[i].contract_address; let master_nullifier_secret_key = self.master_nullifier_secret_keys[i]; - // First we check that derived public key matches master nullifier public key from request - let master_nullifier_public_key = master_nullifier_secret_key.derive_public_key(); - assert( - master_nullifier_public_key.eq(request.master_nullifier_public_key), "Failed to derive matching master nullifier public key from the secret key." - ); + if !is_empty(master_nullifier_secret_key) { + // First we check that derived public key matches master nullifier public key from request + let master_nullifier_public_key = master_nullifier_secret_key.derive_public_key(); + assert( + master_nullifier_public_key.eq(request.master_nullifier_public_key), "Failed to derive matching master nullifier public key from the secret key." + ); - // Then we check that siloing the master secret key with the contract address gives the app nullifier secret key + // Then we check that siloing the master secret key with the contract address gives the app nullifier secret key - let app_nullifier_secret_key = poseidon2_hash( - [ - master_nullifier_secret_key.high, master_nullifier_secret_key.low, contract_address.to_field(), GENERATOR_INDEX__NSK_M - ] - ); - assert( - app_nullifier_secret_key.eq(request.app_nullifier_secret_key), "Failed to derive matching app nullifier secret key from the secret key." - ); + let app_nullifier_secret_key = poseidon2_hash( + [ + master_nullifier_secret_key.high, master_nullifier_secret_key.low, contract_address.to_field(), GENERATOR_INDEX__NSK_M + ] + ); + assert( + app_nullifier_secret_key.eq(request.app_nullifier_secret_key), "Failed to derive matching app nullifier secret key from the secret key." + ); + } else { + should_propagate[i] = true; + } } } + + filter_array_to_bounded_vec(requests, should_propagate) } } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr index a9bef5dadc9..090d9aae72e 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr @@ -1,7 +1,8 @@ // This will be moved to a separate Read Request Reset Circuit. use dep::types::{ abis::{read_request::ScopedReadRequest, side_effect::Readable}, - merkle_tree::{assert_check_membership, LeafPreimage, MembershipWitness}, traits::{Empty, is_empty} + merkle_tree::{assert_check_membership, LeafPreimage, MembershipWitness}, traits::{Empty, is_empty}, + utils::arrays::filter_array_to_bounded_vec }; struct ReadRequestStateEnum { @@ -102,13 +103,13 @@ fn propagate_unverified_read_requests BoundedVec where T: ReadValueHint, S: ReadValueHint { - let mut propagated_read_requests = BoundedVec::new(); + let mut should_propagate = [false; READ_REQUEST_LEN]; + for i in 0..READ_REQUEST_LEN { - let read_request = read_requests[i]; - if !is_empty(read_request) { + if !is_empty(read_requests[i]) { let status = read_request_statuses[i]; if status.state == ReadRequestState.NADA { - propagated_read_requests.push(read_request); + should_propagate[i] = true; } else if status.state == ReadRequestState.PENDING { assert( pending_read_hints[status.hint_index].read_request_index() == i, "Hinted pending read request does not match status" @@ -120,7 +121,7 @@ fn propagate_unverified_read_requests( 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 7f547e5b351..a1d2159d279 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 @@ -8,7 +8,7 @@ use crate::{ kernel_data::KernelData, public_kernel_data::PublicKernelData, max_block_number::MaxBlockNumber, private_kernel_data::PrivateKernelData, note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, - nullifier_key_validation_request::ScopedNullifierKeyValidationRequest, + nullifier_key_validation_request::{ScopedNullifierKeyValidationRequest, NullifierKeyValidationRequest}, public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, read_request::{ReadRequest, ScopedReadRequest}, side_effect::SideEffect, validation_requests::{ValidationRequests, ValidationRequestsBuilder} @@ -25,7 +25,8 @@ use crate::{ hash::silo_nullifier, header::Header, messaging::l2_to_l1_message::{L2ToL1Message, ScopedL2ToL1Message}, partial_state_reference::PartialStateReference, tests::fixtures, transaction::tx_context::TxContext, - traits::Empty, recursion::{verification_key::VerificationKey, proof::NestedRecursiveProof} + traits::Empty, recursion::{verification_key::VerificationKey, proof::NestedRecursiveProof}, + grumpkin_point::GrumpkinPoint }; struct FixtureBuilder { @@ -394,6 +395,18 @@ impl FixtureBuilder { new_read_request_index } + pub fn add_request_for_nullifier_key_validation( + &mut self, + master_nullifier_public_key: GrumpkinPoint, + app_nullifier_secret_key: Field + ) -> u64 { + let new_request_index = self.nullifier_key_validation_requests.len(); + let request = NullifierKeyValidationRequest { master_nullifier_public_key, app_nullifier_secret_key }; + self.nullifier_key_validation_requests.push(request.scope(self.storage_contract_address)); + + new_request_index + } + pub fn set_encrypted_logs(&mut self, hash: Field, preimages_length: Field) { let side_effect = SideEffect { value: hash, counter: self.next_counter() }; self.encrypted_logs_hashes.push(side_effect); 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 a84baf83a2a..34e7f941d0d 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 @@ -13,6 +13,31 @@ pub fn array_to_bounded_vec(array: [T; N]) -> BoundedVec where T: Em BoundedVec { storage: array, len } } +unconstrained fn filter_array_to_bounded_vec_unsafe(arr: [T; N], should_propagate: [bool; N]) -> BoundedVec { + let mut vec = BoundedVec::new(); + for i in 0..N { + if should_propagate[i] { + vec.push(arr[i]); + } + } + vec +} + +pub fn filter_array_to_bounded_vec(arr: [T; N], should_propagate: [bool; N]) -> BoundedVec where T: Eq { + let vec_hint = filter_array_to_bounded_vec_unsafe(arr, should_propagate); + let mut verifying_index = 0; + + for i in 0..N { + if should_propagate[i] { + assert_eq(arr[i], vec_hint.get(verifying_index)); + verifying_index += 1; + } + } + assert_eq(verifying_index, vec_hint.len()); + + vec_hint +} + // Routine which validates that all zero values of an array form a contiguous region at the end, i.e., // of the form: [*,*,*...,0,0,0,0] where any * is non-zero. Note that a full array of non-zero values is // valid. diff --git a/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts b/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts index 977200103a8..cef282a7064 100644 --- a/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts +++ b/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts @@ -6,6 +6,7 @@ import { type PrivateKernelCircuitPublicInputs, type PrivateKernelInitCircuitPrivateInputs, type PrivateKernelInnerCircuitPrivateInputs, + type PrivateKernelResetCircuitPrivateInputs, type PrivateKernelTailCircuitPrivateInputs, type PrivateKernelTailCircuitPublicInputs, Proof, @@ -25,6 +26,8 @@ import { convertPrivateKernelInitOutputsFromWitnessMap, convertPrivateKernelInnerInputsToWitnessMap, convertPrivateKernelInnerOutputsFromWitnessMap, + convertPrivateKernelResetInputsToWitnessMap, + convertPrivateKernelResetOutputsFromWitnessMap, convertPrivateKernelTailForPublicOutputsFromWitnessMap, convertPrivateKernelTailInputsToWitnessMap, convertPrivateKernelTailOutputsFromWitnessMap, @@ -69,6 +72,9 @@ const PrivateKernelArtifactMapping: Record> { + const witnessMap = convertPrivateKernelResetInputsToWitnessMap(inputs); + return await this.createSafeProof(witnessMap, 'PrivateKernelResetArtifact'); + } + public async createProofTail( inputs: PrivateKernelTailCircuitPrivateInputs, ): Promise> { diff --git a/yarn-project/circuit-types/src/interfaces/proof_creator.ts b/yarn-project/circuit-types/src/interfaces/proof_creator.ts index 5181864cbb1..9c66dcbdcb1 100644 --- a/yarn-project/circuit-types/src/interfaces/proof_creator.ts +++ b/yarn-project/circuit-types/src/interfaces/proof_creator.ts @@ -4,6 +4,7 @@ import { type PrivateKernelCircuitPublicInputs, type PrivateKernelInitCircuitPrivateInputs, type PrivateKernelInnerCircuitPrivateInputs, + type PrivateKernelResetCircuitPrivateInputs, type PrivateKernelTailCircuitPrivateInputs, type PrivateKernelTailCircuitPublicInputs, type RECURSIVE_PROOF_LENGTH, @@ -77,6 +78,16 @@ export interface ProofCreator { privateKernelInputsInner: PrivateKernelInnerCircuitPrivateInputs, ): Promise>; + /** + * Creates a proof output by resetting the arrays using the reset circuit. + * + * @param privateKernelInputsTail - The private input data structure for the reset circuit. + * @returns A Promise resolving to a ProofOutput object containing public inputs and the kernel proof. + */ + createProofReset( + privateKernelInputsReset: PrivateKernelResetCircuitPrivateInputs, + ): Promise>; + /** * Creates a proof output based on the last inner kernel iteration kernel data for the final ordering iteration. * diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index 6c28b6087eb..ca21b3b6704 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -57,6 +57,7 @@ export type CircuitName = | 'root-rollup' | 'merge-rollup' | 'private-kernel-inner' + | 'private-kernel-reset' | 'public-kernel-setup' | 'public-kernel-app-logic' | 'public-kernel-teardown' diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 56a6ad52c66..138baa2d6c9 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -23,6 +23,7 @@ export * from './kernel/private_kernel_init_circuit_private_inputs.js'; export * from './kernel/private_kernel_circuit_public_inputs.js'; export * from './kernel/private_kernel_data.js'; export * from './kernel/private_kernel_inner_circuit_private_inputs.js'; +export * from './kernel/private_kernel_reset_circuit_private_inputs.js'; export * from './kernel/private_kernel_tail_circuit_private_inputs.js'; export * from './kernel/private_kernel_tail_circuit_public_inputs.js'; export * from './kernel/public_call_data.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts new file mode 100644 index 00000000000..05da039bbe6 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts @@ -0,0 +1,130 @@ +import { GrumpkinScalar } from '@aztec/foundation/fields'; +import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, +} from '../../constants.gen.js'; +import { type GrumpkinPrivateKey } from '../../types/grumpkin_private_key.js'; +import { countAccumulatedItems } from '../../utils/index.js'; +import { ScopedNoteHash } from '../note_hash.js'; +import { ScopedNullifier } from '../nullifier.js'; +import { + type NoteHashReadRequestHints, + type NullifierReadRequestHints, + noteHashReadRequestHintsFromBuffer, + nullifierReadRequestHintsFromBuffer, +} from '../read_request_hints/index.js'; +import { PrivateKernelData } from './private_kernel_data.js'; + +export class PrivateKernelResetOutputs { + constructor( + public noteHashes: Tuple, + public nullifiers: Tuple, + ) {} + + toBuffer() { + return serializeToBuffer(this.noteHashes, this.nullifiers); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelResetOutputs( + reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, ScopedNoteHash), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, ScopedNullifier), + ); + } +} + +export class PrivateKernelResetHints { + constructor( + /** + * Contains hints for the transient note hashes to locate corresponding nullifiers. + */ + public transientNullifierIndexesForNoteHashes: Tuple, + /** + * Contains hints for the transient nullifiers to locate corresponding note hashes. + */ + public transientNoteHashIndexesForNullifiers: Tuple, + /** + * Contains hints for the transient read requests to localize corresponding commitments. + */ + public noteHashReadRequestHints: NoteHashReadRequestHints, + /** + * Contains hints for the nullifier read requests to locate corresponding pending or settled nullifiers. + */ + public nullifierReadRequestHints: NullifierReadRequestHints, + + /** + * The master nullifier secret keys for the nullifier key validation requests. + */ + public masterNullifierSecretKeys: Tuple, + ) {} + + toBuffer() { + return serializeToBuffer( + this.transientNullifierIndexesForNoteHashes, + this.transientNoteHashIndexesForNullifiers, + this.noteHashReadRequestHints, + this.nullifierReadRequestHints, + this.masterNullifierSecretKeys, + ); + } + + /** + * 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 PrivateKernelResetHints( + reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), + reader.readNumbers(MAX_NEW_NULLIFIERS_PER_TX), + reader.readObject({ fromBuffer: noteHashReadRequestHintsFromBuffer }), + reader.readObject({ fromBuffer: nullifierReadRequestHintsFromBuffer }), + reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar), + ); + } +} + +/** + * Input to the private kernel circuit - reset call. + */ +export class PrivateKernelResetCircuitPrivateInputs { + constructor( + /** + * The previous kernel data + */ + public previousKernel: PrivateKernelData, + public outputs: PrivateKernelResetOutputs, + public hints: PrivateKernelResetHints, + ) {} + + isForPublic() { + return countAccumulatedItems(this.previousKernel.publicInputs.end.publicCallStack) > 0; + } + + /** + * Serialize this as a buffer. + * @returns The buffer. + */ + toBuffer() { + return serializeToBuffer(this.previousKernel, this.outputs, this.hints); + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelResetCircuitPrivateInputs { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelResetCircuitPrivateInputs( + reader.readObject(PrivateKernelData), + reader.readObject(PrivateKernelResetOutputs), + reader.readObject(PrivateKernelResetHints), + ); + } +} 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 36ecd40f5c7..a9d1be68988 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,68 +1,19 @@ -import { GrumpkinScalar } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { MAX_ENCRYPTED_LOGS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, } from '../../constants.gen.js'; -import { type GrumpkinPrivateKey } from '../../types/grumpkin_private_key.js'; import { countAccumulatedItems } from '../../utils/index.js'; import { ScopedNoteHash } from '../note_hash.js'; import { ScopedNullifier } from '../nullifier.js'; -import { - type NoteHashReadRequestHints, - type NullifierReadRequestHints, - noteHashReadRequestHintsFromBuffer, - nullifierReadRequestHintsFromBuffer, -} from '../read_request_hints/index.js'; import { SideEffect } from '../side_effects.js'; import { PrivateKernelData } from './private_kernel_data.js'; -export class PrivateKernelTailOutputs { - constructor( - public noteHashes: Tuple, - public nullifiers: Tuple, - ) {} - - toBuffer() { - return serializeToBuffer(this.noteHashes, this.nullifiers); - } - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new PrivateKernelTailOutputs( - reader.readArray(MAX_NEW_NOTE_HASHES_PER_TX, ScopedNoteHash), - reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, ScopedNullifier), - ); - } -} - export class PrivateKernelTailHints { constructor( - /** - * Contains hints for the transient note hashes to locate corresponding nullifiers. - */ - public transientNullifierIndexesForNoteHashes: Tuple, - /** - * Contains hints for the transient nullifiers to locate corresponding note hashes. - */ - public transientNoteHashIndexesForNullifiers: Tuple, - /** - * Contains hints for the transient read requests to localize corresponding commitments. - */ - public noteHashReadRequestHints: NoteHashReadRequestHints, - /** - * Contains hints for the nullifier read requests to locate corresponding pending or settled nullifiers. - */ - public nullifierReadRequestHints: NullifierReadRequestHints, - - /** - * The master nullifier secret keys for the nullifier key validation requests. - */ - public masterNullifierSecretKeys: Tuple, /* * The sorted new note hashes. */ @@ -99,11 +50,6 @@ export class PrivateKernelTailHints { toBuffer() { return serializeToBuffer( - this.transientNullifierIndexesForNoteHashes, - this.transientNoteHashIndexesForNullifiers, - this.noteHashReadRequestHints, - this.nullifierReadRequestHints, - this.masterNullifierSecretKeys, this.sortedNewNoteHashes, this.sortedNewNoteHashesIndexes, this.sortedNewNullifiers, @@ -123,11 +69,6 @@ export class PrivateKernelTailHints { static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); return new PrivateKernelTailHints( - reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), - reader.readNumbers(MAX_NEW_NULLIFIERS_PER_TX), - reader.readObject({ fromBuffer: noteHashReadRequestHintsFromBuffer }), - reader.readObject({ fromBuffer: nullifierReadRequestHintsFromBuffer }), - reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar), 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), @@ -149,7 +90,6 @@ export class PrivateKernelTailCircuitPrivateInputs { * The previous kernel data */ public previousKernel: PrivateKernelData, - public outputs: PrivateKernelTailOutputs, public hints: PrivateKernelTailHints, ) {} @@ -162,7 +102,7 @@ export class PrivateKernelTailCircuitPrivateInputs { * @returns The buffer. */ toBuffer() { - return serializeToBuffer(this.previousKernel, this.outputs, this.hints); + return serializeToBuffer(this.previousKernel, this.hints); } /** @@ -174,7 +114,6 @@ export class PrivateKernelTailCircuitPrivateInputs { const reader = BufferReader.asReader(buffer); return new PrivateKernelTailCircuitPrivateInputs( reader.readObject(PrivateKernelData), - reader.readObject(PrivateKernelTailOutputs), reader.readObject(PrivateKernelTailHints), ); } diff --git a/yarn-project/noir-protocol-circuits-types/src/index.ts b/yarn-project/noir-protocol-circuits-types/src/index.ts index 1f68ee651a9..48be3f815e4 100644 --- a/yarn-project/noir-protocol-circuits-types/src/index.ts +++ b/yarn-project/noir-protocol-circuits-types/src/index.ts @@ -9,6 +9,7 @@ import { type PrivateKernelCircuitPublicInputs, type PrivateKernelInitCircuitPrivateInputs, type PrivateKernelInnerCircuitPrivateInputs, + type PrivateKernelResetCircuitPrivateInputs, type PrivateKernelTailCircuitPrivateInputs, type PrivateKernelTailCircuitPublicInputs, type PublicKernelCircuitPrivateInputs, @@ -33,6 +34,8 @@ import PrivateKernelInitJson from './target/private_kernel_init.json' assert { t import PrivateKernelInitSimulatedJson from './target/private_kernel_init_simulated.json' assert { type: 'json' }; import PrivateKernelInnerJson from './target/private_kernel_inner.json' assert { type: 'json' }; import PrivateKernelInnerSimulatedJson from './target/private_kernel_inner_simulated.json' assert { type: 'json' }; +import PrivateKernelResetJson from './target/private_kernel_reset.json' assert { type: 'json' }; +import PrivateKernelResetSimulatedJson from './target/private_kernel_reset_simulated.json' assert { type: 'json' }; import PrivateKernelTailJson from './target/private_kernel_tail.json' assert { type: 'json' }; import PrivateKernelTailSimulatedJson from './target/private_kernel_tail_simulated.json' assert { type: 'json' }; import PrivateKernelTailToPublicJson from './target/private_kernel_tail_to_public.json' assert { type: 'json' }; @@ -55,6 +58,7 @@ import { mapPrivateKernelCircuitPublicInputsFromNoir, mapPrivateKernelInitCircuitPrivateInputsToNoir, mapPrivateKernelInnerCircuitPrivateInputsToNoir, + mapPrivateKernelResetCircuitPrivateInputsToNoir, mapPrivateKernelTailCircuitPrivateInputsToNoir, mapPrivateKernelTailCircuitPublicInputsForPublicFromNoir, mapPrivateKernelTailCircuitPublicInputsForRollupFromNoir, @@ -74,11 +78,13 @@ import { type RollupMergeReturnType as MergeRollupReturnType, type PublicKernelAppLogicReturnType as PublicPublicPreviousReturnType, type PublicKernelSetupReturnType as PublicSetupReturnType, + type PrivateKernelResetReturnType as ResetReturnType, type ParityRootReturnType as RootParityReturnType, type RollupRootReturnType as RootRollupReturnType, type PrivateKernelTailReturnType as TailReturnType, PrivateKernelInit as executePrivateKernelInitWithACVM, PrivateKernelInner as executePrivateKernelInnerWithACVM, + PrivateKernelReset as executePrivateKernelResetWithACVM, PrivateKernelTailToPublic as executePrivateKernelTailToPublicWithACVM, PrivateKernelTail as executePrivateKernelTailWithACVM, } from './types/index.js'; @@ -102,6 +108,8 @@ export const PrivateKernelInitArtifact = PrivateKernelInitJson as NoirCompiledCi export const PrivateKernelInnerArtifact = PrivateKernelInnerJson as NoirCompiledCircuit; +export const PrivateKernelResetArtifact = PrivateKernelResetJson as NoirCompiledCircuit; + export const PrivateKernelTailArtifact = PrivateKernelTailJson as NoirCompiledCircuit; export const PrivateKernelTailToPublicArtifact = PrivateKernelTailToPublicJson as NoirCompiledCircuit; @@ -148,6 +156,7 @@ export type ServerProtocolArtifact = export type ClientProtocolArtifact = | 'PrivateKernelInitArtifact' | 'PrivateKernelInnerArtifact' + | 'PrivateKernelResetArtifact' | 'PrivateKernelTailArtifact' | 'PrivateKernelTailToPublicArtifact'; @@ -168,6 +177,7 @@ export const ServerCircuitArtifacts: Record = { PrivateKernelInitArtifact: PrivateKernelInitArtifact, PrivateKernelInnerArtifact: PrivateKernelInnerArtifact, + PrivateKernelResetArtifact: PrivateKernelResetArtifact, PrivateKernelTailArtifact: PrivateKernelTailArtifact, PrivateKernelTailToPublicArtifact: PrivateKernelTailToPublicArtifact, }; @@ -175,6 +185,7 @@ export const ClientCircuitArtifacts: Record = { PrivateKernelInitArtifact: PrivateKernelInitArtifact, PrivateKernelInnerArtifact: PrivateKernelInnerArtifact, + PrivateKernelResetArtifact: PrivateKernelResetArtifact, PrivateKernelTailArtifact: PrivateKernelTailArtifact, PrivateKernelTailToPublicArtifact: PrivateKernelTailToPublicArtifact, PublicKernelSetupArtifact: PublicKernelSetupArtifact, @@ -222,6 +233,23 @@ export async function executeInner( return mapPrivateKernelCircuitPublicInputsFromNoir(returnType); } +/** + * Executes the inner private kernel. + * @param privateKernelResetCircuitPrivateInputs - The private inputs to the reset private kernel. + * @returns The public inputs. + */ +export async function executeReset( + privateKernelResetCircuitPrivateInputs: PrivateKernelResetCircuitPrivateInputs, +): Promise { + const returnType = await executePrivateKernelResetWithACVM( + mapPrivateKernelResetCircuitPrivateInputsToNoir(privateKernelResetCircuitPrivateInputs), + PrivateKernelResetSimulatedJson as CompiledCircuit, + foreignCallHandler, + ); + + return mapPrivateKernelCircuitPublicInputsFromNoir(returnType); +} + /** * Executes the tail private kernel. * @param privateKernelCircuitPrivateInputs - The private inputs to the tail private kernel. @@ -282,6 +310,19 @@ export function convertPrivateKernelInnerInputsToWitnessMap( return initialWitnessMap; } +/** + * Converts the inputs of the private kernel reset circuit into a witness map + * @param inputs - The private kernel inputs. + * @returns The witness map + */ +export function convertPrivateKernelResetInputsToWitnessMap( + privateKernelResetCircuitPrivateInputs: PrivateKernelResetCircuitPrivateInputs, +): WitnessMap { + const mapped = mapPrivateKernelResetCircuitPrivateInputsToNoir(privateKernelResetCircuitPrivateInputs); + const initialWitnessMap = abiEncode(PrivateKernelResetArtifact.abi as Abi, { input: mapped as any }); + return initialWitnessMap; +} + /** * Converts the inputs of the private kernel tail circuit into a witness map * @param inputs - The private kernel inputs. @@ -338,6 +379,21 @@ export function convertPrivateKernelInnerOutputsFromWitnessMap(outputs: WitnessM return mapPrivateKernelCircuitPublicInputsFromNoir(returnType); } +/** + * Converts the outputs of the private kernel reset circuit from a witness map. + * @param outputs - The private kernel outputs as a witness map. + * @returns The public inputs. + */ +export function convertPrivateKernelResetOutputsFromWitnessMap(outputs: WitnessMap): PrivateKernelCircuitPublicInputs { + // Decode the witness map into two fields, the return values and the inputs + const decodedInputs: DecodedInputs = abiDecode(PrivateKernelResetArtifact.abi as Abi, outputs); + + // Cast the inputs as the return type + const returnType = decodedInputs.return_value as ResetReturnType; + + return mapPrivateKernelCircuitPublicInputsFromNoir(returnType); +} + /** * Converts the outputs of the private kernel tail circuit from a witness map. * @param outputs - The private kernel outputs as a witness map. diff --git a/yarn-project/noir-protocol-circuits-types/src/scripts/generate_ts_from_abi.ts b/yarn-project/noir-protocol-circuits-types/src/scripts/generate_ts_from_abi.ts index cb316899245..51581bd197d 100644 --- a/yarn-project/noir-protocol-circuits-types/src/scripts/generate_ts_from_abi.ts +++ b/yarn-project/noir-protocol-circuits-types/src/scripts/generate_ts_from_abi.ts @@ -12,6 +12,7 @@ const circuits = [ 'parity_root', 'private_kernel_init', 'private_kernel_inner', + 'private_kernel_reset', 'private_kernel_tail', 'private_kernel_tail_to_public', 'public_kernel_setup', 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 ba9787b5840..380d7ee8e0d 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -75,10 +75,12 @@ import { type PrivateKernelInitHints, type PrivateKernelInnerCircuitPrivateInputs, type PrivateKernelInnerHints, + type PrivateKernelResetCircuitPrivateInputs, + type PrivateKernelResetHints, + type PrivateKernelResetOutputs, type PrivateKernelTailCircuitPrivateInputs, PrivateKernelTailCircuitPublicInputs, type PrivateKernelTailHints, - type PrivateKernelTailOutputs, PublicAccumulatedData, type PublicCallData, type PublicCallStackItem, @@ -179,9 +181,11 @@ import type { PrivateKernelInitHints as PrivateKernelInitHintsNoir, PrivateKernelInnerCircuitPrivateInputs as PrivateKernelInnerCircuitPrivateInputsNoir, PrivateKernelInnerHints as PrivateKernelInnerHintsNoir, + PrivateKernelResetCircuitPrivateInputs as PrivateKernelResetCircuitPrivateInputsNoir, + PrivateKernelResetHints as PrivateKernelResetHintsNoir, + PrivateKernelResetOutputs as PrivateKernelResetOutputsNoir, PrivateKernelTailCircuitPrivateInputs as PrivateKernelTailCircuitPrivateInputsNoir, PrivateKernelTailHints as PrivateKernelTailHintsNoir, - PrivateKernelTailOutputs as PrivateKernelTailOutputsNoir, PrivateKernelTailToPublicCircuitPrivateInputs as PrivateKernelTailToPublicCircuitPrivateInputsNoir, PublicAccumulatedData as PublicAccumulatedDataNoir, PublicCallData as PublicCallDataNoir, @@ -1415,7 +1419,7 @@ export function mapPrivateKernelInnerCircuitPrivateInputsToNoir( }; } -function mapPrivateKernelTailOutputsToNoir(inputs: PrivateKernelTailOutputs): PrivateKernelTailOutputsNoir { +function mapPrivateKernelResetOutputsToNoir(inputs: PrivateKernelResetOutputs): PrivateKernelResetOutputsNoir { return { note_hashes: mapTuple(inputs.noteHashes, mapScopedNoteHashToNoir), nullifiers: mapTuple(inputs.nullifiers, mapScopedNullifierToNoir), @@ -1424,14 +1428,6 @@ function mapPrivateKernelTailOutputsToNoir(inputs: PrivateKernelTailOutputs): Pr function mapPrivateKernelTailHintsToNoir(inputs: PrivateKernelTailHints): PrivateKernelTailHintsNoir { return { - transient_nullifier_indexes_for_note_hashes: mapTuple( - inputs.transientNullifierIndexesForNoteHashes, - mapNumberToNoir, - ), - transient_note_hash_indexes_for_nullifiers: mapTuple(inputs.transientNoteHashIndexesForNullifiers, mapNumberToNoir), - note_hash_read_request_hints: mapNoteHashReadRequestHintsToNoir(inputs.noteHashReadRequestHints), - nullifier_read_request_hints: mapNullifierReadRequestHintsToNoir(inputs.nullifierReadRequestHints), - master_nullifier_secret_keys: mapTuple(inputs.masterNullifierSecretKeys, mapGrumpkinPrivateKeyToNoir), sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapScopedNoteHashToNoir), sorted_new_note_hashes_indexes: mapTuple(inputs.sortedNewNoteHashesIndexes, mapNumberToNoir), sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapScopedNullifierToNoir), @@ -1443,12 +1439,34 @@ function mapPrivateKernelTailHintsToNoir(inputs: PrivateKernelTailHints): Privat }; } +function mapPrivateKernelResetHintsToNoir(inputs: PrivateKernelResetHints): PrivateKernelResetHintsNoir { + return { + transient_nullifier_indexes_for_note_hashes: mapTuple( + inputs.transientNullifierIndexesForNoteHashes, + mapNumberToNoir, + ), + transient_note_hash_indexes_for_nullifiers: mapTuple(inputs.transientNoteHashIndexesForNullifiers, mapNumberToNoir), + note_hash_read_request_hints: mapNoteHashReadRequestHintsToNoir(inputs.noteHashReadRequestHints), + nullifier_read_request_hints: mapNullifierReadRequestHintsToNoir(inputs.nullifierReadRequestHints), + master_nullifier_secret_keys: mapTuple(inputs.masterNullifierSecretKeys, mapGrumpkinPrivateKeyToNoir), + }; +} + +export function mapPrivateKernelResetCircuitPrivateInputsToNoir( + inputs: PrivateKernelResetCircuitPrivateInputs, +): PrivateKernelResetCircuitPrivateInputsNoir { + return { + previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), + outputs: mapPrivateKernelResetOutputsToNoir(inputs.outputs), + hints: mapPrivateKernelResetHintsToNoir(inputs.hints), + }; +} + export function mapPrivateKernelTailCircuitPrivateInputsToNoir( inputs: PrivateKernelTailCircuitPrivateInputs, ): PrivateKernelTailCircuitPrivateInputsNoir { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - outputs: mapPrivateKernelTailOutputsToNoir(inputs.outputs), hints: mapPrivateKernelTailHintsToNoir(inputs.hints), }; } @@ -1458,7 +1476,6 @@ export function mapPrivateKernelTailToPublicCircuitPrivateInputsToNoir( ): PrivateKernelTailToPublicCircuitPrivateInputsNoir { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - outputs: mapPrivateKernelTailOutputsToNoir(inputs.outputs), hints: mapPrivateKernelTailHintsToNoir(inputs.hints), }; } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index b6d86e6ae70..99983a3bfb3 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -163,6 +163,7 @@ describe('Kernel Prover', () => { ); proofCreator.createProofInit.mockResolvedValue(createProofOutput([])); proofCreator.createProofInner.mockResolvedValue(createProofOutput([])); + proofCreator.createProofReset.mockResolvedValue(createProofOutput([])); proofCreator.createProofTail.mockResolvedValue(createProofOutputFinal([])); proofCreator.createAppCircuitProof.mockResolvedValue(createAppCircuitProofOutput()); diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index 9048a6f9e6e..5d3da7712c4 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -10,6 +10,7 @@ import { PrivateKernelData, PrivateKernelInitCircuitPrivateInputs, PrivateKernelInnerCircuitPrivateInputs, + PrivateKernelResetCircuitPrivateInputs, PrivateKernelTailCircuitPrivateInputs, type PrivateKernelTailCircuitPublicInputs, type RECURSIVE_PROOF_LENGTH, @@ -28,8 +29,9 @@ import { type ExecutionResult, collectNoteHashLeafIndexMap, collectNullifiedNote import { buildPrivateKernelInitHints, buildPrivateKernelInnerHints, + buildPrivateKernelResetHints, + buildPrivateKernelResetOutputs, buildPrivateKernelTailHints, - buildPrivateKernelTailOutputs, } from './private_inputs_builders/index.js'; import { type ProvingDataOracle } from './proving_data_oracle.js'; @@ -126,8 +128,30 @@ export class KernelProver { firstIteration = false; } - const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(output.verificationKey); - const previousKernelData = new PrivateKernelData( + let previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(output.verificationKey); + let previousKernelData = new PrivateKernelData( + output.publicInputs, + output.proof, + output.verificationKey, + Number(previousVkMembershipWitness.leafIndex), + assertLength(previousVkMembershipWitness.siblingPath, VK_TREE_HEIGHT), + ); + + const expectedOutputs = buildPrivateKernelResetOutputs( + output.publicInputs.end.newNoteHashes, + output.publicInputs.end.newNullifiers, + ); + + output = await this.proofCreator.createProofReset( + new PrivateKernelResetCircuitPrivateInputs( + previousKernelData, + expectedOutputs, + await buildPrivateKernelResetHints(output.publicInputs, noteHashLeafIndexMap, this.oracle), + ), + ); + + previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(output.verificationKey); + previousKernelData = new PrivateKernelData( output.publicInputs, output.proof, output.verificationKey, @@ -139,11 +163,9 @@ export class KernelProver { `Calling private kernel tail with hwm ${previousKernelData.publicInputs.minRevertibleSideEffectCounter}`, ); - const hints = await buildPrivateKernelTailHints(output.publicInputs, noteHashLeafIndexMap, this.oracle); - - const expectedOutputs = buildPrivateKernelTailOutputs(hints.sortedNewNoteHashes, hints.sortedNewNullifiers); + const hints = buildPrivateKernelTailHints(output.publicInputs); - const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData, expectedOutputs, hints); + const privateInputs = new PrivateKernelTailCircuitPrivateInputs(previousKernelData, hints); 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_reset_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts new file mode 100644 index 00000000000..a38f81f292b --- /dev/null +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts @@ -0,0 +1,104 @@ +import { + type Fr, + GrumpkinScalar, + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + type MAX_NULLIFIER_READ_REQUESTS_PER_TX, + MembershipWitness, + NULLIFIER_TREE_HEIGHT, + type PrivateKernelCircuitPublicInputs, + PrivateKernelResetHints, + type ScopedNullifier, + type ScopedNullifierKeyValidationRequest, + type ScopedReadRequest, + buildNoteHashReadRequestHints, + buildNullifierReadRequestHints, + buildTransientDataHints, +} from '@aztec/circuits.js'; +import { makeTuple } from '@aztec/foundation/array'; +import { type Tuple } from '@aztec/foundation/serialize'; + +import { type ProvingDataOracle } from '../proving_data_oracle.js'; + +function getNullifierReadRequestHints( + nullifierReadRequests: Tuple, + nullifiers: Tuple, + oracle: ProvingDataOracle, +) { + const getNullifierMembershipWitness = async (nullifier: Fr) => { + const res = await oracle.getNullifierMembershipWitness(nullifier); + if (!res) { + throw new Error(`Cannot find the leaf for nullifier ${nullifier.toBigInt()}.`); + } + + const { index, siblingPath, leafPreimage } = res; + return { + membershipWitness: new MembershipWitness( + NULLIFIER_TREE_HEIGHT, + index, + siblingPath.toTuple(), + ), + leafPreimage, + }; + }; + + return buildNullifierReadRequestHints({ getNullifierMembershipWitness }, nullifierReadRequests, nullifiers); +} + +async function getMasterNullifierSecretKeys( + nullifierKeyValidationRequests: Tuple< + ScopedNullifierKeyValidationRequest, + typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + >, + oracle: ProvingDataOracle, +) { + const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero); + for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) { + const request = nullifierKeyValidationRequests[i].request; + if (request.isEmpty()) { + break; + } + keys[i] = await oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey); + } + return keys; +} + +export async function buildPrivateKernelResetHints( + publicInputs: PrivateKernelCircuitPublicInputs, + noteHashLeafIndexMap: Map, + oracle: ProvingDataOracle, +) { + const noteHashReadRequestHints = await buildNoteHashReadRequestHints( + oracle, + publicInputs.validationRequests.noteHashReadRequests, + publicInputs.end.newNoteHashes, + noteHashLeafIndexMap, + ); + + const nullifierReadRequestHints = await getNullifierReadRequestHints( + publicInputs.validationRequests.nullifierReadRequests, + publicInputs.end.newNullifiers, + oracle, + ); + + const masterNullifierSecretKeys = await getMasterNullifierSecretKeys( + publicInputs.validationRequests.nullifierKeyValidationRequests, + oracle, + ); + + const [transientNullifierIndexesForNoteHashes, transientNoteHashIndexesForNullifiers] = buildTransientDataHints( + publicInputs.end.newNoteHashes, + publicInputs.end.newNullifiers, + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + ); + + return new PrivateKernelResetHints( + transientNullifierIndexesForNoteHashes, + transientNoteHashIndexesForNullifiers, + noteHashReadRequestHints, + nullifierReadRequestHints, + masterNullifierSecretKeys, + ); +} diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_outputs.ts similarity index 85% rename from yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts rename to yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_outputs.ts index 91d8cd8be0a..e2b80f6f0f2 100644 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_outputs.ts @@ -1,14 +1,14 @@ import { MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - PrivateKernelTailOutputs, + PrivateKernelResetOutputs, ScopedNoteHash, ScopedNullifier, } from '@aztec/circuits.js'; import { padArrayEnd } from '@aztec/foundation/collection'; import { type Tuple } from '@aztec/foundation/serialize'; -export function buildPrivateKernelTailOutputs( +export function buildPrivateKernelResetOutputs( prevNoteHashes: Tuple, prevNullifiers: Tuple, ) { @@ -26,5 +26,5 @@ export function buildPrivateKernelTailOutputs( MAX_NEW_NULLIFIERS_PER_TX, ); - return new PrivateKernelTailOutputs(noteHashes, nullifiers); + return new PrivateKernelResetOutputs(noteHashes, nullifiers); } diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts index efef31f153a..8b30244e83b 100644 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts @@ -1,31 +1,16 @@ import { - type Fr, - GrumpkinScalar, type MAX_ENCRYPTED_LOGS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, - type MAX_NULLIFIER_READ_REQUESTS_PER_TX, type MAX_UNENCRYPTED_LOGS_PER_TX, - MembershipWitness, - NULLIFIER_TREE_HEIGHT, type PrivateKernelCircuitPublicInputs, PrivateKernelTailHints, - type ScopedNullifier, - type ScopedNullifierKeyValidationRequest, - type ScopedReadRequest, type SideEffect, type SideEffectType, - buildNoteHashReadRequestHints, - buildNullifierReadRequestHints, - buildTransientDataHints, sortByCounterGetSortedHints, } from '@aztec/circuits.js'; -import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; -import { type ProvingDataOracle } from '../proving_data_oracle.js'; - /** @deprecated Use sortByCounterGetSortedHints instead */ function sortSideEffects( sideEffects: Tuple, @@ -48,72 +33,7 @@ function sortSideEffects( return [sorted.map(({ sideEffect }) => sideEffect) as Tuple, originalToSorted as Tuple]; } -function getNullifierReadRequestHints( - nullifierReadRequests: Tuple, - nullifiers: Tuple, - oracle: ProvingDataOracle, -) { - const getNullifierMembershipWitness = async (nullifier: Fr) => { - const res = await oracle.getNullifierMembershipWitness(nullifier); - if (!res) { - throw new Error(`Cannot find the leaf for nullifier ${nullifier.toBigInt()}.`); - } - - const { index, siblingPath, leafPreimage } = res; - return { - membershipWitness: new MembershipWitness( - NULLIFIER_TREE_HEIGHT, - index, - siblingPath.toTuple(), - ), - leafPreimage, - }; - }; - - return buildNullifierReadRequestHints({ getNullifierMembershipWitness }, nullifierReadRequests, nullifiers); -} - -async function getMasterNullifierSecretKeys( - nullifierKeyValidationRequests: Tuple< - ScopedNullifierKeyValidationRequest, - typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX - >, - oracle: ProvingDataOracle, -) { - const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero); - for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) { - const request = nullifierKeyValidationRequests[i].request; - if (request.isEmpty()) { - break; - } - keys[i] = await oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey); - } - return keys; -} - -export async function buildPrivateKernelTailHints( - publicInputs: PrivateKernelCircuitPublicInputs, - noteHashLeafIndexMap: Map, - oracle: ProvingDataOracle, -) { - const noteHashReadRequestHints = await buildNoteHashReadRequestHints( - oracle, - publicInputs.validationRequests.noteHashReadRequests, - publicInputs.end.newNoteHashes, - noteHashLeafIndexMap, - ); - - const nullifierReadRequestHints = await getNullifierReadRequestHints( - publicInputs.validationRequests.nullifierReadRequests, - publicInputs.end.newNullifiers, - oracle, - ); - - const masterNullifierSecretKeys = await getMasterNullifierSecretKeys( - publicInputs.validationRequests.nullifierKeyValidationRequests, - oracle, - ); - +export function buildPrivateKernelTailHints(publicInputs: PrivateKernelCircuitPublicInputs) { const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints( publicInputs.end.newNoteHashes, MAX_NEW_NOTE_HASHES_PER_TX, @@ -134,19 +54,7 @@ export async function buildPrivateKernelTailHints( typeof MAX_UNENCRYPTED_LOGS_PER_TX >(publicInputs.end.unencryptedLogsHashes); - const [transientNullifierIndexesForNoteHashes, transientNoteHashIndexesForNullifiers] = buildTransientDataHints( - sortedNoteHashes, - sortedNullifiers, - MAX_NEW_NOTE_HASHES_PER_TX, - MAX_NEW_NULLIFIERS_PER_TX, - ); - return new PrivateKernelTailHints( - transientNullifierIndexesForNoteHashes, - transientNoteHashIndexesForNullifiers, - noteHashReadRequestHints, - nullifierReadRequestHints, - masterNullifierSecretKeys, sortedNoteHashes, sortedNoteHashesIndexes, sortedNullifiers, 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 477ce51240f..de292d85327 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,4 +1,5 @@ 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 { buildPrivateKernelTailOutputs } from './build_private_kernel_tail_outputs.js'; +export { buildPrivateKernelResetHints } from './build_private_kernel_reset_hints.js'; +export { buildPrivateKernelResetOutputs } from './build_private_kernel_reset_outputs.js'; diff --git a/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts b/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts index ee18de6092d..ce5ce55a1cf 100644 --- a/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts @@ -6,6 +6,7 @@ import { type PrivateKernelCircuitPublicInputs, type PrivateKernelInitCircuitPrivateInputs, type PrivateKernelInnerCircuitPrivateInputs, + type PrivateKernelResetCircuitPrivateInputs, type PrivateKernelTailCircuitPrivateInputs, type PrivateKernelTailCircuitPublicInputs, RECURSIVE_PROOF_LENGTH, @@ -15,7 +16,13 @@ import { import { siloNoteHash } from '@aztec/circuits.js/hash'; import { createDebugLogger } from '@aztec/foundation/log'; import { elapsed } from '@aztec/foundation/timer'; -import { executeInit, executeInner, executeTail, executeTailForPublic } from '@aztec/noir-protocol-circuits-types'; +import { + executeInit, + executeInner, + executeReset, + executeTail, + executeTailForPublic, +} from '@aztec/noir-protocol-circuits-types'; /** * Test Proof Creator executes circuit simulations and provides fake proofs. @@ -59,6 +66,20 @@ export class TestProofCreator implements ProofCreator { return this.makeEmptyKernelProofOutput(result); } + public async createProofReset( + privateInputs: PrivateKernelResetCircuitPrivateInputs, + ): Promise> { + const [duration, result] = await elapsed(() => executeReset(privateInputs)); + this.log.debug(`Simulated private kernel reset`, { + eventName: 'circuit-simulation', + circuitName: 'private-kernel-reset', + duration, + inputSize: privateInputs.toBuffer().length, + outputSize: result.toBuffer().length, + } satisfies CircuitSimulationStats); + return this.makeEmptyKernelProofOutput(result); + } + public async createProofTail( privateInputs: PrivateKernelTailCircuitPrivateInputs, ): Promise> {