From ed6df8e4f8f8242c01df2cc763833a45a144f57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Wed, 15 May 2024 19:02:08 +0200 Subject: [PATCH] feat: Add first version of kernel reset circuit (#6393) This PR moves the resetting logic from tail circuits to their own circuit, the first version of the reset. Right now it resets the whole TX and is just run before the tail. The idea is to make the data structures of this reset generic and create different sizes of it, and start using it when necessary between the iterations of the inner kernel circuit. --- .../noir-protocol-circuits/Nargo.toml | 2 + .../kernel_circuit_public_inputs_composer.nr | 54 +-- .../crates/private-kernel-lib/src/lib.nr | 2 + .../src/private_kernel_reset.nr | 427 ++++++++++++++++++ .../src/private_kernel_tail.nr | 288 ++---------- .../src/private_kernel_tail_to_public.nr | 280 +----------- .../private-kernel-reset-simulated/Nargo.toml | 9 + .../src/main.nr | 6 + .../crates/private-kernel-reset/Nargo.toml | 9 + .../crates/private-kernel-reset/src/main.nr | 7 + .../private-kernel-tail-simulated/src/main.nr | 2 +- .../crates/private-kernel-tail/src/main.nr | 2 +- .../private_validation_request_processor.nr | 85 ++-- .../src/reset/read_request.nr | 13 +- .../crates/types/src/tests/fixture_builder.nr | 17 +- .../crates/types/src/utils/arrays.nr | 25 + .../src/prover/bb_native_proof_creator.ts | 13 + .../src/interfaces/proof_creator.ts | 11 + yarn-project/circuit-types/src/stats/stats.ts | 1 + yarn-project/circuits.js/src/structs/index.ts | 1 + ...ate_kernel_reset_circuit_private_inputs.ts | 130 ++++++ ...vate_kernel_tail_circuit_private_inputs.ts | 63 +-- .../noir-protocol-circuits-types/src/index.ts | 56 +++ .../src/scripts/generate_ts_from_abi.ts | 1 + .../src/type_conversion.ts | 43 +- .../src/kernel_prover/kernel_prover.test.ts | 1 + .../pxe/src/kernel_prover/kernel_prover.ts | 36 +- .../build_private_kernel_reset_hints.ts | 104 +++++ ... => build_private_kernel_reset_outputs.ts} | 6 +- .../build_private_kernel_tail_hints.ts | 94 +--- .../private_inputs_builders/index.ts | 3 +- .../kernel_prover/test/test_circuit_prover.ts | 23 +- 32 files changed, 1038 insertions(+), 776 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/Nargo.toml create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-reset/Nargo.toml create mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr create mode 100644 yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts create mode 100644 yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts rename yarn-project/pxe/src/kernel_prover/private_inputs_builders/{build_private_kernel_tail_outputs.ts => build_private_kernel_reset_outputs.ts} (85%) 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> {