From d279897b457a805788171ab3a8fee518427ce6ff Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Sun, 3 Mar 2024 16:23:51 +0000 Subject: [PATCH 01/10] Add nullifier read requests to public kernel. --- .../src/core/libraries/ConstantsGen.sol | 2 +- .../aztec/src/context/private_context.nr | 1 + .../aztec/src/context/public_context.nr | 15 ++++-- .../crates/public-kernel-lib/src/common.nr | 47 ++++++++++++++++--- ...accumulated_non_revertible_data_builder.nr | 6 ++- .../public_accumulated_non_revertible_data.nr | 5 +- .../src/abis/public_circuit_public_inputs.nr | 17 +++++-- .../crates/types/src/constants.nr | 2 +- .../crates/types/src/contract_class_id.nr | 6 --- .../public_circuit_public_inputs_builder.nr | 9 ++-- yarn-project/circuits.js/src/constants.gen.ts | 2 +- .../kernel/combined_accumulated_data.ts | 7 +++ .../structs/public_circuit_public_inputs.ts | 11 +++++ .../circuits.js/src/tests/factories.ts | 24 +++++----- .../src/type_conversion.ts | 3 ++ .../src/sequencer/abstract_phase_manager.ts | 7 +++ .../simulator/src/public/execution.ts | 3 ++ yarn-project/simulator/src/public/executor.ts | 3 ++ 18 files changed, 129 insertions(+), 41 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 622bace99a5e..2cea63ad3f47 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -112,7 +112,7 @@ library Constants { uint256 internal constant PARTIAL_STATE_REFERENCE_LENGTH = 8; uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = 223; uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 218; - uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 194; + uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 198; uint256 internal constant STATE_REFERENCE_LENGTH = 10; uint256 internal constant TX_CONTEXT_DATA_LENGTH = 11; uint256 internal constant TX_REQUEST_LENGTH = 17; diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 7f6757f74558..478533274956 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -455,6 +455,7 @@ impl PrivateContext { call_context: reader.read_struct(CallContext::deserialize), args_hash: reader.read(), return_values: [0; RETURN_VALUES_LENGTH], + nullifier_read_requests: [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL], contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL], contract_storage_reads: [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL], public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL], diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 810934586f97..c7d8a7ed645b 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -7,7 +7,7 @@ use dep::protocol_types::{ global_variables::GlobalVariables, function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs, public_call_stack_item::PublicCallStackItem, - public_circuit_public_inputs::PublicCircuitPublicInputs, + public_circuit_public_inputs::PublicCircuitPublicInputs, read_request::ReadRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash} }, address::{AztecAddress, EthAddress}, @@ -15,7 +15,7 @@ use dep::protocol_types::{ MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, hash::hash_args, header::Header, messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader @@ -28,6 +28,7 @@ struct PublicContext { args_hash : Field, return_values : BoundedVec, + nullifier_read_requests: BoundedVec, contract_storage_update_requests: BoundedVec, contract_storage_reads: BoundedVec, public_call_stack_hashes: BoundedVec, @@ -91,13 +92,12 @@ impl ContextInterface for PublicContext { impl PublicContext { pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext { - let empty_storage_read = StorageRead::empty(); - let empty_storage_update = StorageUpdateRequest::empty(); PublicContext { inputs, side_effect_counter: inputs.call_context.start_side_effect_counter, args_hash, return_values: BoundedVec::new(), + nullifier_read_requests: BoundedVec::new(), contract_storage_update_requests: BoundedVec::new(), contract_storage_reads: BoundedVec::new(), public_call_stack_hashes: BoundedVec::new(), @@ -143,6 +143,7 @@ impl PublicContext { let pub_circuit_pub_inputs = PublicCircuitPublicInputs { call_context: self.inputs.call_context, // Done args_hash: self.args_hash, // Done + nullifier_read_requests: self.nullifier_read_requests.storage, contract_storage_update_requests: self.contract_storage_update_requests.storage, contract_storage_reads: self.contract_storage_reads.storage, return_values: self.return_values.storage, @@ -158,6 +159,12 @@ impl PublicContext { pub_circuit_pub_inputs } + pub fn push_nullifier_read_request(&mut self, nullifier: Field) { + let request = ReadRequest { value: nullifier, counter: self.side_effect_counter }; + self.nullifier_read_requests.push(request); + self.side_effect_counter = self.side_effect_counter + 1; + } + pub fn message_portal(&mut self, recipient: EthAddress, content: Field) { let message = L2ToL1Message { recipient, content }; self.new_l2_to_l1_msgs.push(message); diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr index bd27c381283a..5f861cd9ea4a 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr @@ -1,20 +1,19 @@ use dep::types::{ abis::{ call_request::CallRequest, public_call_stack_item::PublicCallStackItem, - accumulated_data::{CombinedAccumulatedData, CombinedAccumulatedDataBuilder}, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputsBuilder, new_contract_data::NewContractData, kernel_data::{PrivateKernelTailData, PublicKernelData}, - public_call_data::PublicCallData, public_circuit_public_inputs::PublicCircuitPublicInputs, - public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, + public_call_data::PublicCallData, public_data_read::PublicDataRead, + public_data_update_request::PublicDataUpdateRequest, read_request::ReadRequestContext, side_effect::{SideEffect, SideEffectLinkedToNoteHash} }, address::AztecAddress, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, constants::{ MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - MAX_PUBLIC_DATA_READS_PER_CALL, NUM_FIELDS_PER_SHA256, MAX_REVERTIBLE_PUBLIC_DATA_READS_PER_TX, - MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_CALL, NUM_FIELDS_PER_SHA256, + MAX_REVERTIBLE_PUBLIC_DATA_READS_PER_TX, MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX }, hash::{silo_note_hash, silo_nullifier, compute_l2_to_l1_hash, accumulate_sha256}, @@ -60,6 +59,7 @@ pub fn initialize_end_values( circuit_outputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); circuit_outputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(start.new_l2_to_l1_msgs); + circuit_outputs.end.nullifier_read_requests = array_to_bounded_vec(start.nullifier_read_requests); circuit_outputs.end.public_data_update_requests = array_to_bounded_vec(start.public_data_update_requests); circuit_outputs.end.public_data_reads = array_to_bounded_vec(start.public_data_reads); @@ -74,6 +74,7 @@ pub fn initialize_end_values( circuit_outputs.end_non_revertible.new_nullifiers = array_to_bounded_vec(start_non_revertible.new_nullifiers); circuit_outputs.end_non_revertible.public_call_stack = array_to_bounded_vec(start_non_revertible.public_call_stack); + circuit_outputs.end_non_revertible.nullifier_read_requests = array_to_bounded_vec(start_non_revertible.nullifier_read_requests); circuit_outputs.end_non_revertible.public_data_update_requests = array_to_bounded_vec(start_non_revertible.public_data_update_requests); circuit_outputs.end_non_revertible.public_data_reads = array_to_bounded_vec(start_non_revertible.public_data_reads); } @@ -147,6 +148,7 @@ pub fn update_public_end_non_revertible_values( validate_call_requests(public_call_requests, hashes, public_call); circuit_outputs.end_non_revertible.public_call_stack.extend_from_bounded_vec(public_call_requests); + propagate_nullifier_read_requests_non_revertible(public_call, circuit_outputs); propagate_new_nullifiers_non_revertible(public_call, circuit_outputs); propagate_new_note_hashes_non_revertible(public_call, circuit_outputs); propagate_valid_non_revertible_public_data_update_requests(public_call, circuit_outputs); @@ -167,6 +169,7 @@ pub fn update_public_end_values(public_call: PublicCallData, circuit_outputs: &m validate_call_requests(public_call_requests, hashes, public_call); circuit_outputs.end.public_call_stack.extend_from_bounded_vec(public_call_requests); + propagate_nullifier_read_requests_revertible(public_call, circuit_outputs); propagate_new_nullifiers(public_call, circuit_outputs); propagate_new_note_hashes(public_call, circuit_outputs); @@ -177,6 +180,38 @@ pub fn update_public_end_values(public_call: PublicCallData, circuit_outputs: &m propagate_valid_public_data_reads(public_call, circuit_outputs); } +fn propagate_nullifier_read_requests_non_revertible( + public_call: PublicCallData, + circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder +) { + let public_call_public_inputs = public_call.call_stack_item.public_inputs; + let nullifier_read_requests = public_call_public_inputs.nullifier_read_requests; + let storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; + + for i in 0..MAX_NULLIFIER_READ_REQUESTS_PER_CALL { + let request = nullifier_read_requests[i]; + if !is_empty(request) { + circuit_outputs.end_non_revertible.nullifier_read_requests.push(request.to_context(storage_contract_address)); + } + } +} + +fn propagate_nullifier_read_requests_revertible( + public_call: PublicCallData, + circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder +) { + let public_call_public_inputs = public_call.call_stack_item.public_inputs; + let nullifier_read_requests = public_call_public_inputs.nullifier_read_requests; + let storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; + + for i in 0..MAX_NULLIFIER_READ_REQUESTS_PER_CALL { + let request = nullifier_read_requests[i]; + if !is_empty(request) { + circuit_outputs.end.nullifier_read_requests.push(request.to_context(storage_contract_address)); + } + } +} + fn propagate_valid_public_data_update_requests( public_call: PublicCallData, circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_non_revertible_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_non_revertible_data_builder.nr index fea3f3bde43f..f138d7e1f266 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_non_revertible_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_non_revertible_data_builder.nr @@ -5,20 +5,21 @@ use crate::{ public_accumulated_non_revertible_data::PublicAccumulatedNonRevertibleData }, call_request::CallRequest, public_data_read::PublicDataRead, - public_data_update_request::PublicDataUpdateRequest, + public_data_update_request::PublicDataUpdateRequest, read_request::ReadRequestContext, side_effect::{SideEffect, SideEffectLinkedToNoteHash} } }; use crate::constants::{ MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX, MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX, - MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX }; struct AccumulatedNonRevertibleDataBuilder { new_note_hashes: BoundedVec, new_nullifiers: BoundedVec, public_call_stack: BoundedVec, + nullifier_read_requests: BoundedVec, public_data_update_requests: BoundedVec, public_data_reads: BoundedVec, } @@ -36,6 +37,7 @@ impl AccumulatedNonRevertibleDataBuilder { new_note_hashes: self.new_note_hashes.storage, new_nullifiers: self.new_nullifiers.storage, public_call_stack: self.public_call_stack.storage, + nullifier_read_requests: self.nullifier_read_requests.storage, public_data_update_requests: self.public_data_update_requests.storage, public_data_reads: self.public_data_reads.storage } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_non_revertible_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_non_revertible_data.nr index 4df61024f02b..697c307e656a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_non_revertible_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_non_revertible_data.nr @@ -1,14 +1,14 @@ use crate::{ abis::{ call_request::CallRequest, public_data_read::PublicDataRead, - public_data_update_request::PublicDataUpdateRequest, + public_data_update_request::PublicDataUpdateRequest, read_request::ReadRequestContext, side_effect::{SideEffect, SideEffectLinkedToNoteHash} } }; use crate::constants::{ MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX, MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX, - MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX }; use dep::std::unsafe; @@ -20,6 +20,7 @@ struct PublicAccumulatedNonRevertibleData { new_note_hashes: [SideEffect; MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX], new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX], public_call_stack: [CallRequest; MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX], + nullifier_read_requests: [ReadRequestContext; MAX_NULLIFIER_READ_REQUESTS_PER_TX], public_data_update_requests: [PublicDataUpdateRequest; MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], public_data_reads: [PublicDataRead; MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX], } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr index 92968c80264d..0879063fc638 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr @@ -1,11 +1,15 @@ use crate::{ - abis::{call_context::CallContext, side_effect::{SideEffect, SideEffectLinkedToNoteHash}}, + abis::{ + call_context::CallContext, read_request::ReadRequest, + side_effect::{SideEffect, SideEffectLinkedToNoteHash} +}, address::AztecAddress, constants::{ MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH, - GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS, PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, + RETURN_VALUES_LENGTH, GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS, + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, hash::pedersen_hash, header::Header, messaging::l2_to_l1_message::L2ToL1Message, @@ -18,6 +22,7 @@ struct PublicCircuitPublicInputs{ args_hash: Field, return_values: [Field; RETURN_VALUES_LENGTH], + nullifier_read_requests: [ReadRequest; MAX_NULLIFIER_READ_REQUESTS_PER_CALL], contract_storage_update_requests: [StorageUpdateRequest; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL], contract_storage_reads: [StorageRead; MAX_PUBLIC_DATA_READS_PER_CALL], @@ -52,6 +57,9 @@ impl Serialize for PublicCircuitPublicInput fields.extend_from_array(self.call_context.serialize()); fields.push(self.args_hash); fields.extend_from_array(self.return_values); + for i in 0..MAX_NULLIFIER_READ_REQUESTS_PER_CALL { + fields.extend_from_array(self.nullifier_read_requests[i].serialize()); + } for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL { fields.extend_from_array(self.contract_storage_update_requests[i].serialize()); } @@ -85,6 +93,7 @@ impl Deserialize for PublicCircuitPublicInp call_context: reader.read_struct(CallContext::deserialize), args_hash: reader.read(), return_values: reader.read_array([0; RETURN_VALUES_LENGTH]), + nullifier_read_requests: reader.read_struct_array(ReadRequest::deserialize, [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL]), contract_storage_update_requests: reader.read_struct_array(StorageUpdateRequest::deserialize, [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL]), contract_storage_reads: reader.read_struct_array(StorageRead::deserialize, [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL]), public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 85f4433e2e3e..f0951ea80e11 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -165,7 +165,7 @@ global PRIVATE_CALL_STACK_ITEM_LENGTH: u64 = 223; // constant as well PRIVATE_CALL_STACK_ITEM_LENGTH global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 218; // Change this ONLY if you have changed the PublicCircuitPublicInputs structure. -global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 194; +global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = 198; global STATE_REFERENCE_LENGTH: u64 = 10; // 2 for snap + 8 for partial global TX_CONTEXT_DATA_LENGTH: u64 = 11; global TX_REQUEST_LENGTH: u64 = 17; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr index 69ffb16ecdfe..2b6c09ce8020 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr @@ -1,10 +1,4 @@ -use crate::abis::{ - function_data::FunctionData, private_circuit_public_inputs::PrivateCircuitPublicInputs, - public_circuit_public_inputs::PublicCircuitPublicInputs -}; -use crate::address::AztecAddress; use crate::constants::{GENERATOR_INDEX__CONTRACT_LEAF}; -use crate::traits::Hash; struct ContractClassId { inner: Field diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr index 8e4e7923f1f6..071e6c32c5a1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr @@ -1,7 +1,7 @@ use crate::{ abis::{ call_context::CallContext, public_circuit_public_inputs::PublicCircuitPublicInputs, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + read_request::ReadRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash} }, address::AztecAddress, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, header::Header, @@ -9,14 +9,16 @@ use crate::{ }; use crate::constants::{ MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, + RETURN_VALUES_LENGTH }; struct PublicCircuitPublicInputsBuilder { call_context: CallContext, args_hash: Field, return_values: BoundedVec, + nullifier_read_requests: BoundedVec, contract_storage_update_requests: BoundedVec, contract_storage_reads: BoundedVec, public_call_stack_hashes: BoundedVec, @@ -42,6 +44,7 @@ impl PublicCircuitPublicInputsBuilder { call_context: self.call_context, args_hash: self.args_hash, return_values: self.return_values.storage, + nullifier_read_requests: self.nullifier_read_requests.storage, contract_storage_update_requests: self.contract_storage_update_requests.storage, contract_storage_reads: self.contract_storage_reads.storage, public_call_stack_hashes: self.public_call_stack_hashes.storage, diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index a53f6979ca6e..5ac058bc2b2a 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -97,7 +97,7 @@ export const NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_LENGTH = 5; export const PARTIAL_STATE_REFERENCE_LENGTH = 8; export const PRIVATE_CALL_STACK_ITEM_LENGTH = 223; export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 218; -export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 194; +export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 198; export const STATE_REFERENCE_LENGTH = 10; export const TX_CONTEXT_DATA_LENGTH = 11; export const TX_REQUEST_LENGTH = 17; diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index 246f48b89831..aabc5088e1cc 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -714,6 +714,10 @@ export class PrivateAccumulatedNonRevertibleData { export class PublicAccumulatedNonRevertibleData { constructor( + /** + * The nullifier read requests made in this transaction. + */ + public nullifierReadRequests: Tuple, /** * The new non-revertible commitments made in this transaction. */ @@ -746,6 +750,7 @@ export class PublicAccumulatedNonRevertibleData { static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); return new this( + reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext), reader.readArray(MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX, SideEffect), reader.readArray(MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash), reader.readArray(MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), @@ -764,6 +769,7 @@ export class PublicAccumulatedNonRevertibleData { static empty() { return new this( + makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext.empty), makeTuple(MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX, SideEffect.empty), makeTuple(MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash.empty), makeTuple(MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), @@ -774,6 +780,7 @@ export class PublicAccumulatedNonRevertibleData { static fromPrivateAccumulatedNonRevertibleData(data: PrivateAccumulatedNonRevertibleData) { return new this( + makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext.empty), data.newNoteHashes, data.newNullifiers, data.publicCallStack, diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts index b04124320481..38dd29413770 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts @@ -11,6 +11,7 @@ import { MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, @@ -23,6 +24,7 @@ import { ContractStorageRead } from './contract_storage_read.js'; import { ContractStorageUpdateRequest } from './contract_storage_update_request.js'; import { Header } from './header.js'; import { L2ToL1Message } from './l2_to_l1_message.js'; +import { ReadRequest } from './read_request.js'; import { SideEffect, SideEffectLinkedToNoteHash } from './side_effects.js'; /** @@ -42,6 +44,10 @@ export class PublicCircuitPublicInputs { * Return values of the call. */ public returnValues: Tuple, + /** + * Nullifier read requests executed during the call. + */ + public nullifierReadRequests: Tuple, /** * Contract storage update requests executed during the call. */ @@ -107,6 +113,7 @@ export class PublicCircuitPublicInputs { CallContext.empty(), Fr.ZERO, makeTuple(RETURN_VALUES_LENGTH, Fr.zero), + makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest.empty), makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, ContractStorageUpdateRequest.empty), makeTuple(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead.empty), makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, Fr.zero), @@ -129,6 +136,7 @@ export class PublicCircuitPublicInputs { this.callContext.isEmpty() && this.argsHash.isZero() && isFrArrayEmpty(this.returnValues) && + isArrayEmpty(this.nullifierReadRequests, item => item.isEmpty()) && isArrayEmpty(this.contractStorageUpdateRequests, item => item.isEmpty()) && isArrayEmpty(this.contractStorageReads, item => item.isEmpty()) && isFrArrayEmpty(this.publicCallStackHashes) && @@ -152,6 +160,7 @@ export class PublicCircuitPublicInputs { fields.callContext, fields.argsHash, fields.returnValues, + fields.nullifierReadRequests, fields.contractStorageUpdateRequests, fields.contractStorageReads, fields.publicCallStackHashes, @@ -194,6 +203,7 @@ export class PublicCircuitPublicInputs { reader.readObject(CallContext), reader.readObject(Fr), reader.readArray(RETURN_VALUES_LENGTH, Fr), + reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, ContractStorageUpdateRequest), reader.readArray(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead), reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, Fr), @@ -214,6 +224,7 @@ export class PublicCircuitPublicInputs { CallContext.fromFields(reader), reader.readField(), reader.readFieldArray(RETURN_VALUES_LENGTH), + reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, ContractStorageUpdateRequest), reader.readArray(MAX_PUBLIC_DATA_READS_PER_CALL, ContractStorageRead), reader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL), diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index f3d0b779a874..f5e98e54ddc2 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -179,6 +179,14 @@ export function makeSelector(seed: number): FunctionSelector { return new FunctionSelector(seed); } +function makeReadRequest(n: number): ReadRequest { + return new ReadRequest(new Fr(BigInt(n)), n + 1); +} + +function makeReadRequestContext(n: number): ReadRequestContext { + return new ReadRequestContext(new Fr(BigInt(n)), n + 1, AztecAddress.fromBigInt(BigInt(n + 2))); +} + /** * Creates arbitrary NullifierKeyValidationRequest from the given seed. * @param seed - The seed to use for generating the NullifierKeyValidationRequest. @@ -263,7 +271,7 @@ export function makeCombinedAccumulatedData(seed = 1, full = false): CombinedAcc return new CombinedAccumulatedData( tupleGenerator(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, sideEffectFromNumber, seed + 0x80), - tupleGenerator(MAX_NULLIFIER_READ_REQUESTS_PER_TX, readRequestContextFromNumber, seed + 0x90), + tupleGenerator(MAX_NULLIFIER_READ_REQUESTS_PER_TX, makeReadRequestContext, seed + 0x90), tupleGenerator( MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, makeNullifierKeyValidationRequestContext, @@ -294,7 +302,7 @@ export function makeCombinedAccumulatedRevertibleData(seed = 1, full = false): P return new PublicAccumulatedRevertibleData( tupleGenerator(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, sideEffectFromNumber, seed + 0x80), - tupleGenerator(MAX_NULLIFIER_READ_REQUESTS_PER_TX, readRequestContextFromNumber, seed + 0x90), + tupleGenerator(MAX_NULLIFIER_READ_REQUESTS_PER_TX, makeReadRequestContext, seed + 0x90), tupleGenerator( MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, makeNullifierKeyValidationRequestContext, @@ -356,6 +364,7 @@ export function makeCombinedAccumulatedNonRevertibleData(seed = 1, full = false) const tupleGenerator = full ? makeTuple : makeHalfFullTuple; return new PublicAccumulatedNonRevertibleData( + tupleGenerator(MAX_NULLIFIER_READ_REQUESTS_PER_TX, makeReadRequestContext, seed + 0x91), tupleGenerator(MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX, sideEffectFromNumber, seed + 0x101), tupleGenerator(MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, sideEffectLinkedFromNumber, seed + 0x201), tupleGenerator(MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x501), @@ -423,6 +432,7 @@ export function makePublicCircuitPublicInputs( makeCallContext(seed, storageContractAddress), fr(seed + 0x100), tupleGenerator(RETURN_VALUES_LENGTH, fr, seed + 0x200), + tupleGenerator(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, makeReadRequest, seed + 0x400), tupleGenerator(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, makeContractStorageUpdateRequest, seed + 0x400), tupleGenerator(MAX_PUBLIC_DATA_READS_PER_CALL, makeContractStorageRead, seed + 0x500), tupleGenerator(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, fr, seed + 0x600), @@ -861,7 +871,7 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn returnValues: makeTuple(RETURN_VALUES_LENGTH, fr, seed + 0x200), minRevertibleSideEffectCounter: fr(0), noteHashReadRequests: makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, sideEffectFromNumber, seed + 0x300), - nullifierReadRequests: makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, readRequestFromNumber, seed + 0x310), + nullifierReadRequests: makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, makeReadRequest, seed + 0x310), nullifierKeyValidationRequests: makeTuple( MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, makeNullifierKeyValidationRequest, @@ -1324,11 +1334,3 @@ export function sideEffectFromNumber(n: number): SideEffect { export function sideEffectLinkedFromNumber(n: number): SideEffectLinkedToNoteHash { return new SideEffectLinkedToNoteHash(new Fr(BigInt(n)), Fr.zero(), Fr.zero()); } - -function readRequestFromNumber(n: number): ReadRequest { - return new ReadRequest(new Fr(BigInt(n)), n + 1); -} - -function readRequestContextFromNumber(n: number): ReadRequestContext { - return new ReadRequestContext(new Fr(BigInt(n)), n + 1, AztecAddress.fromBigInt(BigInt(n + 2))); -} 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 0142e245046c..d9cf6fc46633 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -1231,6 +1231,7 @@ export function mapPublicAccumulatedNonRevertibleDataToNoir( data: PublicAccumulatedNonRevertibleData, ): PublicAccumulatedNonRevertibleDataNoir { return { + nullifier_read_requests: mapTuple(data.nullifierReadRequests, mapSideEffectToNoir), new_note_hashes: mapTuple(data.newNoteHashes, mapSideEffectToNoir), new_nullifiers: mapTuple(data.newNullifiers, mapSideEffectLinkedToNoir), public_call_stack: mapTuple(data.publicCallStack, mapCallRequestToNoir), @@ -1417,6 +1418,7 @@ export function mapPublicAccumulatedNonRevertibleDataFromNoir( data: PublicAccumulatedNonRevertibleDataNoir, ): PublicAccumulatedNonRevertibleData { return new PublicAccumulatedNonRevertibleData( + mapTupleFromNoir(data.nullifier_read_requests, MAX_NULLIFIER_READ_REQUESTS_PER_TX, mapReadRequestContextFromNoir), mapTupleFromNoir(data.new_note_hashes, MAX_NON_REVERTIBLE_NOTE_HASHES_PER_TX, mapSideEffectFromNoir), mapTupleFromNoir(data.new_nullifiers, MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, mapSideEffectLinkedFromNoir), mapTupleFromNoir( @@ -1547,6 +1549,7 @@ export function mapPublicCircuitPublicInputsToNoir( call_context: mapCallContextToNoir(publicInputs.callContext), args_hash: mapFieldToNoir(publicInputs.argsHash), return_values: mapTuple(publicInputs.returnValues, mapFieldToNoir), + nullifier_read_requests: mapTuple(publicInputs.nullifierReadRequests, mapReadRequestToNoir), contract_storage_update_requests: mapTuple( publicInputs.contractStorageUpdateRequests, mapStorageUpdateRequestToNoir, diff --git a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts index b8e9960d1a08..0a8770297f05 100644 --- a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts +++ b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts @@ -13,6 +13,7 @@ import { MAX_NEW_NULLIFIERS_PER_CALL, MAX_NON_REVERTIBLE_PUBLIC_DATA_READS_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, @@ -33,6 +34,7 @@ import { PublicKernelCircuitPublicInputs, PublicKernelData, RETURN_VALUES_LENGTH, + ReadRequest, SideEffect, SideEffectLinkedToNoteHash, VK_TREE_HEIGHT, @@ -320,6 +322,11 @@ export abstract class AbstractPhaseManager { newNullifiers: padArrayEnd(result.newNullifiers, SideEffectLinkedToNoteHash.empty(), MAX_NEW_NULLIFIERS_PER_CALL), newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, L2ToL1Message.empty(), MAX_NEW_L2_TO_L1_MSGS_PER_CALL), returnValues: padArrayEnd(result.returnValues, Fr.ZERO, RETURN_VALUES_LENGTH), + nullifierReadRequests: padArrayEnd( + result.nullifierReadRequests, + ReadRequest.empty(), + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, + ), contractStorageReads: padArrayEnd( result.contractStorageReads, ContractStorageRead.empty(), diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index 6d6bca3ee1cb..4e1ee2e444d2 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -9,6 +9,7 @@ import { L2ToL1Message, PublicDataRead, PublicDataUpdateRequest, + ReadRequest, SideEffect, SideEffectLinkedToNoteHash, } from '@aztec/circuits.js'; @@ -28,6 +29,8 @@ export interface PublicExecutionResult { newL2ToL1Messages: L2ToL1Message[]; /** The new nullifiers to be inserted into the nullifier tree. */ newNullifiers: SideEffectLinkedToNoteHash[]; + /** The nullifier read requests emitted in this call. */ + nullifierReadRequests: ReadRequest[]; /** The contract storage reads performed by the function. */ contractStorageReads: ContractStorageRead[]; /** The contract storage update requests performed by the function. */ diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index 814570d016b0..a68f3c9a0e51 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -51,11 +51,13 @@ export async function executePublicFunction( const returnWitness = extractReturnWitness(acir, partialWitness); const { returnValues, + nullifierReadRequests: nullifierReadRequestsPadded, newL2ToL1Msgs, newNoteHashes: newNoteHashesPadded, newNullifiers: newNullifiersPadded, } = PublicCircuitPublicInputs.fromFields(returnWitness); + const nullifierReadRequests = nullifierReadRequestsPadded.filter(v => !v.isEmpty()); const newL2ToL1Messages = newL2ToL1Msgs.filter(v => !v.isEmpty()); const newNoteHashes = newNoteHashesPadded.filter(v => !v.isEmpty()); const newNullifiers = newNullifiersPadded.filter(v => !v.isEmpty()); @@ -81,6 +83,7 @@ export async function executePublicFunction( newNoteHashes, newL2ToL1Messages, newNullifiers, + nullifierReadRequests, contractStorageReads, contractStorageUpdateRequests, returnValues, From 9be067170921b15ac3b81b475d71f8510caad0cf Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 4 Mar 2024 16:17:08 +0000 Subject: [PATCH 02/10] Add public tail kernel. --- .../noir-protocol-circuits/Nargo.toml | 2 + .../crates/public-kernel-lib/src/lib.nr | 2 + .../src/public_kernel_tail.nr | 79 +++++++++++++++++++ .../public-kernel-tail-simulated/Nargo.toml | 9 +++ .../public-kernel-tail-simulated/src/main.nr | 6 ++ .../crates/public-kernel-tail/Nargo.toml | 9 +++ .../crates/public-kernel-tail/src/main.nr | 6 ++ yarn-project/circuit-types/src/stats/stats.ts | 3 +- yarn-project/circuits.js/src/structs/index.ts | 1 + ...blic_kernel_tail_circuit_private_inputs.ts | 19 +++++ .../noir-protocol-circuits-types/src/index.ts | 48 +++++++++++ .../src/scripts/generate_ts_from_abi.ts | 1 + .../src/type_conversion.ts | 10 +++ .../src/sequencer/abstract_phase_manager.ts | 33 +++++++- .../src/sequencer/phase_manager_factory.ts | 12 +++ .../src/sequencer/public_processor.test.ts | 2 + .../src/sequencer/tail_phase_manager.ts | 65 +++++++++++++++ .../sequencer-client/src/simulator/index.ts | 7 ++ .../src/simulator/public_kernel.ts | 26 +++++- .../src/avm/temporary_executor_migration.ts | 3 + 20 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/src/main.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-tail/Nargo.toml create mode 100644 noir-projects/noir-protocol-circuits/crates/public-kernel-tail/src/main.nr create mode 100644 yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts create mode 100644 yarn-project/sequencer-client/src/sequencer/tail_phase_manager.ts diff --git a/noir-projects/noir-protocol-circuits/Nargo.toml b/noir-projects/noir-protocol-circuits/Nargo.toml index 749f1ecda0f0..3be5d5293946 100644 --- a/noir-projects/noir-protocol-circuits/Nargo.toml +++ b/noir-projects/noir-protocol-circuits/Nargo.toml @@ -15,6 +15,8 @@ members = [ "crates/public-kernel-app-logic-simulated", "crates/public-kernel-teardown", "crates/public-kernel-teardown-simulated", + "crates/public-kernel-tail", + "crates/public-kernel-tail-simulated", "crates/rollup-lib", "crates/rollup-merge", "crates/rollup-base", diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/lib.nr index 4373405f5812..9a99c84ba69c 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/lib.nr @@ -7,7 +7,9 @@ mod utils; mod public_kernel_setup; mod public_kernel_app_logic; mod public_kernel_teardown; +mod public_kernel_tail; use public_kernel_setup::PublicKernelSetupCircuitPrivateInputs; use public_kernel_app_logic::PublicKernelAppLogicCircuitPrivateInputs; use public_kernel_teardown::PublicKernelTeardownCircuitPrivateInputs; +use public_kernel_tail::PublicKernelTailCircuitPrivateInputs; diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr new file mode 100644 index 000000000000..75dcda9d9140 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -0,0 +1,79 @@ +use crate::common; +use dep::types::abis::{ + kernel_circuit_public_inputs::{PublicKernelCircuitPublicInputs, PublicKernelCircuitPublicInputsBuilder}, + kernel_data::PublicKernelData +}; +use dep::std::unsafe; + +struct PublicKernelTailCircuitPrivateInputs { + previous_kernel: PublicKernelData, +} + +impl PublicKernelTailCircuitPrivateInputs { + fn validate_inputs(self) { + let needs_setup = self.previous_kernel.public_inputs.needs_setup; + assert(needs_setup == false, "Previous kernel needs setup"); + let needs_app_logic = self.previous_kernel.public_inputs.needs_app_logic; + assert(needs_app_logic == false, "Previous kernel needs app logic"); + let needs_teardown = self.previous_kernel.public_inputs.needs_teardown; + assert(needs_teardown == false, "Previous kernel needs teardown"); + } + + fn public_kernel_tail(self) -> PublicKernelCircuitPublicInputs { + // construct the circuit outputs + let mut public_inputs: PublicKernelCircuitPublicInputsBuilder = unsafe::zeroed(); + + // initialise the end state with our provided previous kernel state + common::initialize_end_values(self.previous_kernel, &mut public_inputs); + + // validate the inputs unique to having a previous private kernel + self.validate_inputs(); + + public_inputs.to_inner() + } +} + +mod tests { + use crate::{public_kernel_tail::PublicKernelTailCircuitPrivateInputs}; + use dep::types::{ + abis::{ + kernel_circuit_public_inputs::{PublicKernelCircuitPublicInputs, PublicKernelCircuitPublicInputsBuilder}, + kernel_data::PublicKernelData + }, + tests::{kernel_data_builder::PreviousKernelDataBuilder} + }; + + struct PublicKernelTailCircuitPrivateInputsBuilder { + previous_kernel: PreviousKernelDataBuilder, + } + + impl PublicKernelTailCircuitPrivateInputsBuilder { + pub fn new() -> Self { + let previous_kernel = PreviousKernelDataBuilder::new(true); + + PublicKernelTailCircuitPrivateInputsBuilder { previous_kernel } + } + + pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { + let previous_kernel = self.previous_kernel.to_public_kernel_data(); + + let kernel = PublicKernelTailCircuitPrivateInputs { previous_kernel }; + + kernel.public_kernel_tail() + } + + pub fn succeeded(&mut self) { + let _ = self.execute(); + } + + pub fn failed(&mut self) { + let _ = self.execute(); + } + } + + #[test] + fn public_kernel_circuit_tail_succeeds() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + builder.succeeded(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml new file mode 100644 index 000000000000..698657b684db --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "public_kernel_teardown_simulated" +type = "bin" +authors = [""] +compiler_version = ">=0.18.0" + +[dependencies] +types = { path = "../types" } +public_kernel_lib = { path = "../public-kernel-lib" } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/src/main.nr new file mode 100644 index 000000000000..e225c2d34a16 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/src/main.nr @@ -0,0 +1,6 @@ +use dep::public_kernel_lib::PublicKernelTailCircuitPrivateInputs; +use dep::types::PublicKernelCircuitPublicInputs; + +unconstrained fn main(input: PublicKernelTailCircuitPrivateInputs) -> distinct pub PublicKernelCircuitPublicInputs { + input.public_kernel_tail() +} diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-tail/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail/Nargo.toml new file mode 100644 index 000000000000..9071a4db2acb --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "public_kernel_tail" +type = "bin" +authors = [""] +compiler_version = ">=0.18.0" + +[dependencies] +types = { path = "../types" } +public_kernel_lib = { path = "../public-kernel-lib" } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-tail/src/main.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail/src/main.nr new file mode 100644 index 000000000000..19b9f802f485 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail/src/main.nr @@ -0,0 +1,6 @@ +use dep::public_kernel_lib::PublicKernelTailCircuitPrivateInputs; +use dep::types::PublicKernelCircuitPublicInputs; + +fn main(input: PublicKernelTailCircuitPrivateInputs) -> distinct pub PublicKernelCircuitPublicInputs { + input.public_kernel_tail() +} diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index 8f87e3339053..8ad9f63bcafe 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -62,7 +62,8 @@ export type CircuitSimulationStats = { | 'private-kernel-inner' | 'public-kernel-setup' | 'public-kernel-app-logic' - | 'public-kernel-teardown'; + | 'public-kernel-teardown' + | 'public-kernel-tail'; /** Duration in ms. */ duration: number; /** Size in bytes of circuit inputs. */ diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 106a1e558a08..5ec39a492e0b 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -26,6 +26,7 @@ export * from './kernel/public_call_data.js'; export * from './kernel/public_kernel_circuit_private_inputs.js'; export * from './kernel/public_kernel_circuit_public_inputs.js'; export * from './kernel/public_kernel_data.js'; +export * from './kernel/public_kernel_tail_circuit_private_inputs.js'; export * from './kernel/rollup_kernel_circuit_public_inputs.js'; export * from './kernel/rollup_kernel_data.js'; export * from './l2_to_l1_message.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts new file mode 100644 index 000000000000..9791da9f4804 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts @@ -0,0 +1,19 @@ +import { serializeToBuffer } from '@aztec/foundation/serialize'; + +import { PublicKernelData } from './public_kernel_data.js'; + +/** + * Inputs to the public kernel circuit. + */ +export class PublicKernelTailCircuitPrivateInputs { + constructor( + /** + * Kernels are recursive and this is the data from the previous kernel. + */ + public readonly previousKernel: PublicKernelData, + ) {} + + toBuffer() { + return serializeToBuffer(this.previousKernel); + } +} diff --git a/yarn-project/noir-protocol-circuits-types/src/index.ts b/yarn-project/noir-protocol-circuits-types/src/index.ts index 3389821ab555..8ab20fc72f9a 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 { PrivateKernelTailCircuitPublicInputs, PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, + PublicKernelTailCircuitPrivateInputs, RootRollupInputs, RootRollupPublicInputs, } from '@aztec/circuits.js'; @@ -27,6 +28,8 @@ import PublicKernelAppLogicJson from './target/public_kernel_app_logic.json' ass import PublicKernelAppLogicSimulatedJson from './target/public_kernel_app_logic_simulated.json' assert { type: 'json' }; import PublicKernelSetupJson from './target/public_kernel_setup.json' assert { type: 'json' }; import PublicKernelSetupSimulatedJson from './target/public_kernel_setup_simulated.json' assert { type: 'json' }; +import PublicKernelTailJson from './target/public_kernel_tail.json' assert { type: 'json' }; +import PublicKernelTailSimulatedJson from './target/public_kernel_tail_simulated.json' assert { type: 'json' }; import PublicKernelTeardownJson from './target/public_kernel_teardown.json' assert { type: 'json' }; import PublicKernelTeardownSimulatedJson from './target/public_kernel_teardown_simulated.json' assert { type: 'json' }; import BaseRollupSimulatedJson from './target/rollup_base_simulated.json' assert { type: 'json' }; @@ -43,6 +46,7 @@ import { mapPrivateKernelTailCircuitPublicInputsFromNoir, mapPublicKernelCircuitPrivateInputsToNoir, mapPublicKernelCircuitPublicInputsFromNoir, + mapPublicKernelTailCircuitPrivateInputsToNoir, mapRootRollupInputsToNoir, mapRootRollupPublicInputsFromNoir, } from './type_conversion.js'; @@ -57,6 +61,10 @@ import { InputType as PublicSetupInputType, ReturnType as PublicSetupReturnType, } from './types/public_kernel_setup_types.js'; +import { + InputType as PublicTailInputType, + ReturnType as PublicTailReturnType, +} from './types/public_kernel_tail_types.js'; import { InputType as BaseRollupInputType, ReturnType as BaseRollupReturnType } from './types/rollup_base_types.js'; import { InputType as MergeRollupInputType, ReturnType as MergeRollupReturnType } from './types/rollup_merge_types.js'; import { InputType as RootRollupInputType, ReturnType as RootRollupReturnType } from './types/rollup_root_types.js'; @@ -88,6 +96,8 @@ export const PublicKernelAppLogicArtifact = PublicKernelAppLogicJson as NoirComp export const PublicKernelTeardownArtifact = PublicKernelTeardownJson as NoirCompiledCircuit; +export const PublicKernelTailArtifact = PublicKernelTailJson as NoirCompiledCircuit; + /** * Executes the init private kernel. * @param privateKernelInitCircuitPrivateInputs - The private inputs to the initial private kernel. @@ -198,6 +208,21 @@ export async function executePublicKernelTeardown( return mapPublicKernelCircuitPublicInputsFromNoir(returnType); } +/** + * Executes the public kernel tail. + * @param publicKernelPrivateInputs - The public kernel teardown circuit private inputs. + * @returns The public inputs. + */ +export async function executePublicKernelTail( + privateInputs: PublicKernelTailCircuitPrivateInputs, +): Promise { + const returnType = await executePublicKernelTailWithACVM({ + input: mapPublicKernelTailCircuitPrivateInputsToNoir(privateInputs), + }); + + return mapPublicKernelCircuitPublicInputsFromNoir(returnType); +} + /** * Executes the root rollup. * @param rootRollupInputs - The root rollup inputs. @@ -400,6 +425,29 @@ async function executePublicKernelTeardownWithACVM( return decodedInputs.return_value as PublicPublicPreviousReturnType; } +/** + * Executes the public tail kernel with the given inputs using the acvm. + */ +async function executePublicKernelTailWithACVM(input: PublicTailInputType): Promise { + const initialWitnessMap = abiEncode(PublicKernelTailSimulatedJson.abi as Abi, input as any); + const decodedBytecode = Buffer.from(PublicKernelTailSimulatedJson.bytecode, 'base64'); + // Execute the circuit + const _witnessMap = await executeCircuitWithBlackBoxSolver( + await getSolver(), + decodedBytecode, + initialWitnessMap, + () => { + throw Error('unexpected oracle during execution'); + }, + ); + + // Decode the witness map into two fields, the return values and the inputs + const decodedInputs: DecodedInputs = abiDecode(PublicKernelTailSimulatedJson.abi as Abi, _witnessMap); + + // Cast the inputs as the return type + return decodedInputs.return_value; +} + /** * Executes the root rollup with the given inputs using the acvm. */ 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 63837951472d..5821ec3446f6 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 @@ -204,6 +204,7 @@ const circuits = [ 'public_kernel_setup', 'public_kernel_app_logic', 'public_kernel_teardown', + 'public_kernel_tail', 'rollup_base', 'rollup_merge', 'rollup_root', 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 d9cf6fc46633..b36e2409744b 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -84,6 +84,7 @@ import { PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, PublicKernelData, + PublicKernelTailCircuitPrivateInputs, ReadRequest, ReadRequestContext, ReadRequestStatus, @@ -164,6 +165,7 @@ import { StorageRead as StorageReadNoir, StorageUpdateRequest as StorageUpdateRequestNoir, } from './types/public_kernel_setup_types.js'; +import { PublicKernelTailCircuitPrivateInputs as PublicKernelTailCircuitPrivateInputsNoir } from './types/public_kernel_tail_types.js'; import { ArchiveRootMembershipWitness as ArchiveRootMembershipWitnessNoir, BaseRollupInputs as BaseRollupInputsNoir, @@ -1400,6 +1402,14 @@ export function mapPublicKernelCircuitPrivateInputsToNoir( }; } +export function mapPublicKernelTailCircuitPrivateInputsToNoir( + inputs: PublicKernelTailCircuitPrivateInputs, +): PublicKernelTailCircuitPrivateInputsNoir { + return { + previous_kernel: mapPublicKernelDataToNoir(inputs.previousKernel), + }; +} + export function mapPublicKernelCircuitPublicInputsFromNoir( inputs: PublicKernelCircuitPublicInputsNoir, ): PublicKernelCircuitPublicInputs { diff --git a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts index 0a8770297f05..2daaa2cccc94 100644 --- a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts +++ b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts @@ -33,6 +33,7 @@ import { PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, PublicKernelData, + PublicKernelTailCircuitPrivateInputs, RETURN_VALUES_LENGTH, ReadRequest, SideEffect, @@ -64,12 +65,14 @@ export enum PublicKernelPhase { SETUP = 'setup', APP_LOGIC = 'app-logic', TEARDOWN = 'teardown', + TAIL = 'tail', } export const PhaseIsRevertible: Record = { [PublicKernelPhase.SETUP]: false, [PublicKernelPhase.APP_LOGIC]: true, [PublicKernelPhase.TEARDOWN]: false, + [PublicKernelPhase.TAIL]: false, }; export abstract class AbstractPhaseManager { @@ -129,6 +132,7 @@ export abstract class AbstractPhaseManager { [PublicKernelPhase.SETUP]: [], [PublicKernelPhase.APP_LOGIC]: [], [PublicKernelPhase.TEARDOWN]: [], + [PublicKernelPhase.TAIL]: [], }; } @@ -142,12 +146,14 @@ export abstract class AbstractPhaseManager { [PublicKernelPhase.SETUP]: [], [PublicKernelPhase.APP_LOGIC]: publicCallsStack, [PublicKernelPhase.TEARDOWN]: [], + [PublicKernelPhase.TAIL]: [], }; } else { return { [PublicKernelPhase.SETUP]: publicCallsStack.slice(0, firstRevertibleCallIndex - 1), [PublicKernelPhase.APP_LOGIC]: publicCallsStack.slice(firstRevertibleCallIndex), [PublicKernelPhase.TEARDOWN]: [publicCallsStack[firstRevertibleCallIndex - 1]], + [PublicKernelPhase.TAIL]: [], }; } } @@ -243,7 +249,7 @@ export abstract class AbstractPhaseManager { executionStack.push(...result.nestedExecutions); const callData = await this.getPublicCallData(result, isExecutionRequest); - [kernelOutput, kernelProof] = await this.runKernelCircuit(callData, kernelOutput, kernelProof); + [kernelOutput, kernelProof] = await this.runKernelCircuitWithCallData(callData, kernelOutput, kernelProof); if (!enqueuedExecutionResult) { enqueuedExecutionResult = result; @@ -260,22 +266,41 @@ export abstract class AbstractPhaseManager { return [kernelOutput, kernelProof, newUnencryptedFunctionLogs]; } - protected async runKernelCircuit( + public async runKernelCircuit( + previousOutput: PublicKernelCircuitPublicInputs, + previousProof: Proof, + ): Promise<[PublicKernelCircuitPublicInputs, Proof]> { + const output = await this.getKernelCircuitOutput(previousOutput, previousProof); + const proof = await this.publicProver.getPublicKernelCircuitProof(output); + return [output, proof]; + } + + protected async runKernelCircuitWithCallData( callData: PublicCallData, previousOutput: PublicKernelCircuitPublicInputs, previousProof: Proof, ): Promise<[PublicKernelCircuitPublicInputs, Proof]> { - const output = await this.getKernelCircuitOutput(callData, previousOutput, previousProof); + const output = await this.getKernelCircuitOutput(previousOutput, previousProof, callData); const proof = await this.publicProver.getPublicKernelCircuitProof(output); return [output, proof]; } protected getKernelCircuitOutput( - callData: PublicCallData, previousOutput: PublicKernelCircuitPublicInputs, previousProof: Proof, + callData?: PublicCallData, ): Promise { const previousKernel = this.getPreviousKernelData(previousOutput, previousProof); + + if (this.phase === PublicKernelPhase.TAIL) { + const inputs = new PublicKernelTailCircuitPrivateInputs(previousKernel); + return this.publicKernel.publicKernelCircuitTail(inputs); + } + + if (!callData) { + throw new Error(`Cannot run public kernel circuit without call data for phase '${this.phase}'.`); + } + const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData); switch (this.phase) { case PublicKernelPhase.SETUP: diff --git a/yarn-project/sequencer-client/src/sequencer/phase_manager_factory.ts b/yarn-project/sequencer-client/src/sequencer/phase_manager_factory.ts index 845b5cb28a98..129fdc88129a 100644 --- a/yarn-project/sequencer-client/src/sequencer/phase_manager_factory.ts +++ b/yarn-project/sequencer-client/src/sequencer/phase_manager_factory.ts @@ -9,6 +9,7 @@ import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js'; import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js'; import { AppLogicPhaseManager } from './app_logic_phase_manager.js'; import { SetupPhaseManager } from './setup_phase_manager.js'; +import { TailPhaseManager } from './tail_phase_manager.js'; import { TeardownPhaseManager } from './teardown_phase_manager.js'; export class PhaseDidNotChangeError extends Error { @@ -115,6 +116,17 @@ export class PhaseManagerFactory { publicContractsDB, publicStateDB, ); + } else if (currentPhaseManager.phase !== PublicKernelPhase.TAIL) { + return new TailPhaseManager( + db, + publicExecutor, + publicKernel, + publicProver, + globalVariables, + historicalHeader, + publicContractsDB, + publicStateDB, + ); } else { return undefined; } diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index d5013bed5ed1..f6a802d1b81f 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -468,6 +468,7 @@ function makePublicExecutionResultFromRequest(item: PublicCallRequest): PublicEx execution: item, nestedExecutions: [], returnValues: [new Fr(1n)], + nullifierReadRequests: [], newNoteHashes: [], newL2ToL1Messages: [], newNullifiers: [], @@ -495,6 +496,7 @@ function makePublicExecutionResult( nestedExecutions, contractStorageUpdateRequests, returnValues: [], + nullifierReadRequests: [], newNoteHashes: [], newNullifiers: [], newL2ToL1Messages: [], diff --git a/yarn-project/sequencer-client/src/sequencer/tail_phase_manager.ts b/yarn-project/sequencer-client/src/sequencer/tail_phase_manager.ts new file mode 100644 index 000000000000..b0b93ea22861 --- /dev/null +++ b/yarn-project/sequencer-client/src/sequencer/tail_phase_manager.ts @@ -0,0 +1,65 @@ +import { Tx } from '@aztec/circuit-types'; +import { GlobalVariables, Header, Proof, PublicKernelCircuitPublicInputs } from '@aztec/circuits.js'; +import { PublicExecutor, PublicStateDB } from '@aztec/simulator'; +import { MerkleTreeOperations } from '@aztec/world-state'; + +import { PublicProver } from '../prover/index.js'; +import { PublicKernelCircuitSimulator } from '../simulator/index.js'; +import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js'; +import { AbstractPhaseManager, PublicKernelPhase } from './abstract_phase_manager.js'; +import { FailedTx } from './processed_tx.js'; + +/** + * The phase manager responsible for performing the fee preparation phase. + */ +export class TailPhaseManager extends AbstractPhaseManager { + constructor( + protected db: MerkleTreeOperations, + protected publicExecutor: PublicExecutor, + protected publicKernel: PublicKernelCircuitSimulator, + protected publicProver: PublicProver, + protected globalVariables: GlobalVariables, + protected historicalHeader: Header, + protected publicContractsDB: ContractsDataSourcePublicDB, + protected publicStateDB: PublicStateDB, + public readonly phase: PublicKernelPhase = PublicKernelPhase.TAIL, + ) { + super(db, publicExecutor, publicKernel, publicProver, globalVariables, historicalHeader, phase); + } + + async handle( + tx: Tx, + previousPublicKernelOutput: PublicKernelCircuitPublicInputs, + previousPublicKernelProof: Proof, + ): Promise<{ + /** + * the output of the public kernel circuit for this phase + */ + publicKernelOutput: PublicKernelCircuitPublicInputs; + /** + * the proof of the public kernel circuit for this phase + */ + publicKernelProof: Proof; + }> { + this.log(`Processing tx ${tx.getTxHash()}`); + this.log(`Executing tail circuit for tx ${tx.getTxHash()}`); + const [publicKernelOutput, publicKernelProof] = await this.runKernelCircuit( + previousPublicKernelOutput, + previousPublicKernelProof, + ); + + // commit the state updates from this transaction + await this.publicStateDB.commit(); + + return { publicKernelOutput, publicKernelProof }; + } + + async rollback(tx: Tx, err: unknown): Promise { + this.log.warn(`Error processing tx ${tx.getTxHash()}: ${err}`); + await this.publicStateDB.rollback(); + return { + tx, + error: err instanceof Error ? err : new Error('Unknown error'), + }; + } +} diff --git a/yarn-project/sequencer-client/src/simulator/index.ts b/yarn-project/sequencer-client/src/simulator/index.ts index 7bc2504999ee..6d9c5fe7fec0 100644 --- a/yarn-project/sequencer-client/src/simulator/index.ts +++ b/yarn-project/sequencer-client/src/simulator/index.ts @@ -4,6 +4,7 @@ import { MergeRollupInputs, PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, + PublicKernelTailCircuitPrivateInputs, RootRollupInputs, RootRollupPublicInputs, } from '@aztec/circuits.js'; @@ -54,4 +55,10 @@ export interface PublicKernelCircuitSimulator { * @returns The public inputs as outputs of the simulation. */ publicKernelCircuitTeardown(inputs: PublicKernelCircuitPrivateInputs): Promise; + /** + * Simulates the public kernel tail circuit from its inputs. + * @param inputs - Inputs to the circuit. + * @returns The public inputs as outputs of the simulation. + */ + publicKernelCircuitTail(inputs: PublicKernelTailCircuitPrivateInputs): Promise; } diff --git a/yarn-project/sequencer-client/src/simulator/public_kernel.ts b/yarn-project/sequencer-client/src/simulator/public_kernel.ts index 2c67a5d6c051..2c3a954b6db9 100644 --- a/yarn-project/sequencer-client/src/simulator/public_kernel.ts +++ b/yarn-project/sequencer-client/src/simulator/public_kernel.ts @@ -1,10 +1,15 @@ import { CircuitSimulationStats } from '@aztec/circuit-types/stats'; -import { PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs } from '@aztec/circuits.js'; +import { + PublicKernelCircuitPrivateInputs, + PublicKernelCircuitPublicInputs, + PublicKernelTailCircuitPrivateInputs, +} from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { elapsed } from '@aztec/foundation/timer'; import { executePublicKernelAppLogic, executePublicKernelSetup, + executePublicKernelTail, executePublicKernelTeardown, } from '@aztec/noir-protocol-circuits-types'; @@ -81,4 +86,23 @@ export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimu } satisfies CircuitSimulationStats); return result; } + + /** + * Simulates the public kernel tail circuit from its inputs. + * @param input - Inputs to the circuit. + * @returns The public inputs as outputs of the simulation. + */ + public async publicKernelCircuitTail( + input: PublicKernelTailCircuitPrivateInputs, + ): Promise { + const [duration, result] = await elapsed(() => executePublicKernelTail(input)); + this.log(`Simulated public kernel tail circuit`, { + eventName: 'circuit-simulation', + circuitName: 'public-kernel-tail', + duration, + inputSize: input.toBuffer().length, + outputSize: result.toBuffer().length, + } satisfies CircuitSimulationStats); + return result; + } } diff --git a/yarn-project/simulator/src/avm/temporary_executor_migration.ts b/yarn-project/simulator/src/avm/temporary_executor_migration.ts index ad66c6b98855..98c77797b71c 100644 --- a/yarn-project/simulator/src/avm/temporary_executor_migration.ts +++ b/yarn-project/simulator/src/avm/temporary_executor_migration.ts @@ -5,6 +5,7 @@ import { ContractStorageUpdateRequest, GlobalVariables, L2ToL1Message, + ReadRequest, SideEffect, SideEffectLinkedToNoteHash, } from '@aztec/circuits.js'; @@ -91,12 +92,14 @@ export function temporaryConvertAvmResults( // TODO(follow up in pr tree): NOT SUPPORTED YET, make sure hashing and log resolution is done correctly // Disabled. const nestedExecutions: PublicExecutionResult[] = []; + const nullifierReadRequests: ReadRequest[] = []; const newNullifiers: SideEffectLinkedToNoteHash[] = []; const unencryptedLogs = FunctionL2Logs.empty(); const newL2ToL1Messages = newWorldState.newL1Messages.map(() => L2ToL1Message.empty()); return { execution, + nullifierReadRequests, newNoteHashes, newL2ToL1Messages, newNullifiers, From abc0d26b533bcb687f78c9aba698616f4d5ab731 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 6 Mar 2024 17:54:15 +0000 Subject: [PATCH 03/10] Add hints to tail kernel. --- .../crates/private-kernel-lib/Nargo.toml | 1 + .../crates/private-kernel-lib/src/lib.nr | 2 - .../src/private_kernel_tail.nr | 18 +- .../crates/public-kernel-lib/Nargo.toml | 1 + .../crates/public-kernel-lib/src/common.nr | 33 ++-- .../src/public_kernel_tail.nr | 165 ++++++++++++++++-- .../crates/reset-kernel-lib/Nargo.toml | 8 + .../crates/reset-kernel-lib/src/lib.nr | 6 + .../src/nullifier_read_request_reset.nr | 0 .../src/read_request_reset.nr | 0 .../types/src/abis/public_call_stack_item.nr | 4 +- .../src/abis/public_circuit_public_inputs.nr | 2 +- .../types/src/tests/kernel_data_builder.nr | 46 ++++- .../crates/types/src/utils/arrays.nr | 18 ++ .../circuits.js/src/hints/build_hints.test.ts | 120 +++++++++++++ .../circuits.js/src/hints/build_hints.ts | 60 +++++++ yarn-project/circuits.js/src/hints/index.ts | 2 + .../circuits.js/src/hints/utils.test.ts | 160 +++++++++++++++++ yarn-project/circuits.js/src/hints/utils.ts | 60 +++++++ yarn-project/circuits.js/src/index.ts | 1 + .../public_call_stack_item.test.ts.snap | 64 +++---- .../public_circuit_public_inputs.test.ts.snap | 124 ++++++------- ...blic_kernel_tail_circuit_private_inputs.ts | 7 +- .../src/structs/read_request_reset_hints.ts | 6 +- .../src/type_conversion.ts | 1 + yarn-project/pxe/src/kernel_oracle/index.ts | 6 +- .../pxe/src/kernel_prover/hints_builder.ts | 53 ++---- .../src/kernel_prover/proving_data_oracle.ts | 2 +- .../src/sequencer/abstract_phase_manager.ts | 29 +-- .../src/sequencer/hints_builder.ts | 56 ++++++ 30 files changed, 857 insertions(+), 198 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/Nargo.toml create mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr rename noir-projects/noir-protocol-circuits/crates/{private-kernel-lib => reset-kernel-lib}/src/nullifier_read_request_reset.nr (100%) rename noir-projects/noir-protocol-circuits/crates/{private-kernel-lib => reset-kernel-lib}/src/read_request_reset.nr (100%) create mode 100644 yarn-project/circuits.js/src/hints/build_hints.test.ts create mode 100644 yarn-project/circuits.js/src/hints/build_hints.ts create mode 100644 yarn-project/circuits.js/src/hints/index.ts create mode 100644 yarn-project/circuits.js/src/hints/utils.test.ts create mode 100644 yarn-project/circuits.js/src/hints/utils.ts create mode 100644 yarn-project/sequencer-client/src/sequencer/hints_builder.ts diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/Nargo.toml index 4132c7e2b182..2782f8d7b312 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/Nargo.toml +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/Nargo.toml @@ -6,3 +6,4 @@ compiler_version = ">=0.18.0" [dependencies] types = { path = "../types" } +reset_kernel_lib = { path = "../reset-kernel-lib" } 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 d05adaea185d..e892008b41a9 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 @@ -8,5 +8,3 @@ use private_kernel_tail::PrivateKernelTailCircuitPrivateInputs; // TODO: rename to be precise as to what its common to. mod common; -mod read_request_reset; -mod nullifier_read_request_reset; 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 1787a725bc37..dfb76330de73 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,8 +1,6 @@ -use crate::{ - common, nullifier_read_request_reset::NullifierReadRequestResetHints, - read_request_reset::reset_read_requests -}; +use crate::common; use dep::std::{cmp::Eq, option::Option, unsafe}; +use dep::reset_kernel_lib::{NullifierReadRequestResetHints, reset_read_requests}; use dep::types::{ abis::{ call_request::CallRequest, nullifier_key_validation_request::NullifierKeyValidationRequestContext, @@ -109,11 +107,7 @@ impl PrivateKernelTailCircuitPrivateInputs { public_inputs.end.note_hash_read_requests = BoundedVec::new(); } - fn assert_sorted_counters( - original: [T; N], - sorted: [T; N], - indexes: [u64; N] - ) where T: Eq + Ordered + Empty { + fn assert_sorted_counters(original: [T; N], sorted: [T; N], indexes: [u64; N]) where T: Eq + Ordered + Empty { let mut prev_was_empty = false; for i in 0..N { @@ -260,9 +254,9 @@ impl PrivateKernelTailCircuitPrivateInputs { mod tests { use dep::std::{cmp::Eq, unsafe}; - use crate::{ - nullifier_read_request_reset::NullifierReadRequestResetHintsBuilder, - private_kernel_tail::PrivateKernelTailCircuitPrivateInputs, + use crate::{private_kernel_tail::PrivateKernelTailCircuitPrivateInputs}; + use dep::reset_kernel_lib::{ + NullifierReadRequestResetHintsBuilder, read_request_reset::{PendingReadHint, ReadRequestState, ReadRequestStatus} }; use dep::types::constants::{ diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/Nargo.toml index 669bac30a780..6806f1835976 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/Nargo.toml +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/Nargo.toml @@ -6,3 +6,4 @@ compiler_version = ">=0.18.0" [dependencies] types = { path = "../types" } +reset_kernel_lib = { path = "../reset-kernel-lib" } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr index 5f861cd9ea4a..af8149260d2f 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr @@ -41,25 +41,21 @@ pub fn validate_inputs(public_call: PublicCallData) { assert(public_call.bytecode_hash != 0, "Bytecode hash cannot be zero"); } -pub fn initialize_end_values( +// Initialises the circuit outputs with the end state of the previous iteration. +// Skips data that will be checked and cleared in the tail circuit. +pub fn initialize_emitted_end_values( previous_kernel: PublicKernelData, circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder ) { - // Initialises the circuit outputs with the end state of the previous iteration circuit_outputs.constants = previous_kernel.public_inputs.constants; - // Ensure the arrays are the same as previously, before we start pushing more data onto them in other - // functions within this circuit: let start = previous_kernel.public_inputs.end; circuit_outputs.end.new_note_hashes = array_to_bounded_vec(start.new_note_hashes); circuit_outputs.end.new_nullifiers = array_to_bounded_vec(start.new_nullifiers); - - circuit_outputs.end.private_call_stack = array_to_bounded_vec(start.private_call_stack); - circuit_outputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); circuit_outputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(start.new_l2_to_l1_msgs); - circuit_outputs.end.nullifier_read_requests = array_to_bounded_vec(start.nullifier_read_requests); + // TODO - should be propagated only in initialize_end_values() since they will be squashed or cleared in the tail circuit. circuit_outputs.end.public_data_update_requests = array_to_bounded_vec(start.public_data_update_requests); circuit_outputs.end.public_data_reads = array_to_bounded_vec(start.public_data_reads); @@ -72,13 +68,30 @@ pub fn initialize_end_values( let start_non_revertible = previous_kernel.public_inputs.end_non_revertible; circuit_outputs.end_non_revertible.new_note_hashes = array_to_bounded_vec(start_non_revertible.new_note_hashes); circuit_outputs.end_non_revertible.new_nullifiers = array_to_bounded_vec(start_non_revertible.new_nullifiers); - circuit_outputs.end_non_revertible.public_call_stack = array_to_bounded_vec(start_non_revertible.public_call_stack); - circuit_outputs.end_non_revertible.nullifier_read_requests = array_to_bounded_vec(start_non_revertible.nullifier_read_requests); circuit_outputs.end_non_revertible.public_data_update_requests = array_to_bounded_vec(start_non_revertible.public_data_update_requests); circuit_outputs.end_non_revertible.public_data_reads = array_to_bounded_vec(start_non_revertible.public_data_reads); } +// Initialises the circuit outputs with the end state of the previous iteration. +// Includes data that will be checked and cleared in the tail circuit. +pub fn initialize_end_values( + previous_kernel: PublicKernelData, + circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder +) { + initialize_emitted_end_values(previous_kernel, circuit_outputs); + + let start = previous_kernel.public_inputs.end; + + // circuit_outputs.end.private_call_stack = array_to_bounded_vec(start.private_call_stack); // This is enforced in the private tail to always be empty. + circuit_outputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); + circuit_outputs.end.nullifier_read_requests = array_to_bounded_vec(start.nullifier_read_requests); + + let start_non_revertible = previous_kernel.public_inputs.end_non_revertible; + circuit_outputs.end_non_revertible.public_call_stack = array_to_bounded_vec(start_non_revertible.public_call_stack); + circuit_outputs.end_non_revertible.nullifier_read_requests = array_to_bounded_vec(start_non_revertible.nullifier_read_requests); +} + fn perform_static_call_checks(public_call: PublicCallData) { let public_inputs = public_call.call_stack_item.public_inputs; if public_inputs.call_context.is_static_call { diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 75dcda9d9140..ecb56535263c 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -1,63 +1,140 @@ use crate::common; -use dep::types::abis::{ +use dep::reset_kernel_lib::{NullifierReadRequestResetHints, reset_read_requests}; +use dep::types::{ + abis::{ kernel_circuit_public_inputs::{PublicKernelCircuitPublicInputs, PublicKernelCircuitPublicInputsBuilder}, - kernel_data::PublicKernelData + kernel_data::PublicKernelData, side_effect::SideEffectLinkedToNoteHash +}, + constants::MAX_NULLIFIER_READ_REQUESTS_PER_TX, + utils::{arrays::{array_length, array_merge, array_concat}} }; use dep::std::unsafe; struct PublicKernelTailCircuitPrivateInputs { previous_kernel: PublicKernelData, + nullifier_read_request_reset_hints: NullifierReadRequestResetHints, } impl PublicKernelTailCircuitPrivateInputs { fn validate_inputs(self) { - let needs_setup = self.previous_kernel.public_inputs.needs_setup; - assert(needs_setup == false, "Previous kernel needs setup"); - let needs_app_logic = self.previous_kernel.public_inputs.needs_app_logic; - assert(needs_app_logic == false, "Previous kernel needs app logic"); - let needs_teardown = self.previous_kernel.public_inputs.needs_teardown; - assert(needs_teardown == false, "Previous kernel needs teardown"); + let previous_public_inputs = self.previous_kernel.public_inputs; + assert(previous_public_inputs.needs_setup == false, "Previous kernel needs setup"); + assert(previous_public_inputs.needs_app_logic == false, "Previous kernel needs app logic"); + assert(previous_public_inputs.needs_teardown == false, "Previous kernel needs teardown"); + assert_eq( + array_length(previous_public_inputs.end.public_call_stack), 0, "Public call stack must be empty when executing the tail circuit" + ); + assert_eq( + array_length(previous_public_inputs.end_non_revertible.public_call_stack), 0, "Public call stack must be empty when executing the tail circuit" + ); } - fn public_kernel_tail(self) -> PublicKernelCircuitPublicInputs { - // construct the circuit outputs - let mut public_inputs: PublicKernelCircuitPublicInputsBuilder = unsafe::zeroed(); + fn validate_nullifier_read_requests(self, public_inputs: &mut PublicKernelCircuitPublicInputsBuilder) { + let end_non_revertible = self.previous_kernel.public_inputs.end_non_revertible; + let end = self.previous_kernel.public_inputs.end; + + // Total number should not exceed MAX_NULLIFIER_READ_REQUESTS_PER_TX. + let requests = array_merge( + end_non_revertible.nullifier_read_requests, + end.nullifier_read_requests + ); + + let pending_nullifiers: [SideEffectLinkedToNoteHash; MAX_NULLIFIER_READ_REQUESTS_PER_TX] = array_concat(end_non_revertible.new_nullifiers, end.new_nullifiers); + + let hints = self.nullifier_read_request_reset_hints; - // initialise the end state with our provided previous kernel state - common::initialize_end_values(self.previous_kernel, &mut public_inputs); + let nullifier_tree_root = public_inputs.constants.historical_header.state.partial.nullifier_tree.root; + + let unverified_nullifier_read_requests = reset_read_requests( + requests, + pending_nullifiers, + hints.read_request_statuses, + hints.pending_read_hints, + hints.settled_read_hints, + nullifier_tree_root + ); + + assert( + unverified_nullifier_read_requests.len() == 0, "All nullifier read requests must be verified" + ); + } + + pub fn public_kernel_tail(self) -> PublicKernelCircuitPublicInputs { + let mut public_inputs: PublicKernelCircuitPublicInputsBuilder = unsafe::zeroed(); - // validate the inputs unique to having a previous private kernel self.validate_inputs(); + common::initialize_emitted_end_values(self.previous_kernel, &mut public_inputs); + + self.validate_nullifier_read_requests(&mut public_inputs); + public_inputs.to_inner() } } mod tests { use crate::{public_kernel_tail::PublicKernelTailCircuitPrivateInputs}; + use dep::reset_kernel_lib::{ + NullifierReadRequestResetHintsBuilder, + read_request_reset::{PendingReadHint, ReadRequestState, ReadRequestStatus} + }; use dep::types::{ abis::{ kernel_circuit_public_inputs::{PublicKernelCircuitPublicInputs, PublicKernelCircuitPublicInputsBuilder}, kernel_data::PublicKernelData }, + constants::MAX_NULLIFIER_READ_REQUESTS_PER_TX, tests::{kernel_data_builder::PreviousKernelDataBuilder} }; struct PublicKernelTailCircuitPrivateInputsBuilder { previous_kernel: PreviousKernelDataBuilder, + nullifier_read_request_reset_hints_builder: NullifierReadRequestResetHintsBuilder, } impl PublicKernelTailCircuitPrivateInputsBuilder { pub fn new() -> Self { let previous_kernel = PreviousKernelDataBuilder::new(true); - PublicKernelTailCircuitPrivateInputsBuilder { previous_kernel } + PublicKernelTailCircuitPrivateInputsBuilder { + previous_kernel, + nullifier_read_request_reset_hints_builder: NullifierReadRequestResetHintsBuilder::new(MAX_NULLIFIER_READ_REQUESTS_PER_TX) + } + } + + pub fn append_nullifiers(&mut self, num_nullifiers: u64) { + self.previous_kernel.append_new_nullifiers_from_public(num_nullifiers); + } + + pub fn append_nullifiers_non_revertible(&mut self, num_nullifiers: u64) { + self.previous_kernel.append_new_nullifiers_non_revertible_from_public(num_nullifiers); + } + + pub fn add_nullifier_pending_read(&mut self, nullifier_index: u64) { + let read_request_index = self.previous_kernel.add_read_request_for_pending_nullifier(nullifier_index); + let hint_index = self.nullifier_read_request_reset_hints_builder.pending_read_hints.len(); + let pending_value_index = nullifier_index + self.previous_kernel.end_non_revertible.new_nullifiers.len(); + let hint = PendingReadHint { read_request_index, pending_value_index }; + self.nullifier_read_request_reset_hints_builder.pending_read_hints.push(hint); + self.nullifier_read_request_reset_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; + } + + pub fn add_nullifier_pending_read_non_revertible(&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_non_revertible(nullifier_index); + let hint_index = self.nullifier_read_request_reset_hints_builder.pending_read_hints.len(); + let hint = PendingReadHint { read_request_index, pending_value_index: nullifier_index }; + self.nullifier_read_request_reset_hints_builder.pending_read_hints.push(hint); + self.nullifier_read_request_reset_hints_builder.read_request_statuses[read_request_index] = ReadRequestStatus { state: ReadRequestState.PENDING, hint_index }; } pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { let previous_kernel = self.previous_kernel.to_public_kernel_data(); - let kernel = PublicKernelTailCircuitPrivateInputs { previous_kernel }; + let kernel = PublicKernelTailCircuitPrivateInputs { + previous_kernel, + nullifier_read_request_reset_hints: self.nullifier_read_request_reset_hints_builder.to_hints() + }; kernel.public_kernel_tail() } @@ -76,4 +153,60 @@ mod tests { let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); builder.succeeded(); } + + #[test] + unconstrained fn one_pending_nullifier_read_request() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + + builder.append_nullifiers(3); + builder.add_nullifier_pending_read(1); + builder.succeeded(); + } + + #[test] + unconstrained fn two_pending_nullifier_read_requests() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + + builder.append_nullifiers(3); + builder.add_nullifier_pending_read(1); + builder.add_nullifier_pending_read(0); + + builder.succeeded(); + } + + #[test] + unconstrained fn one_pending_nullifier_read_request_non_revertible() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + + builder.append_nullifiers_non_revertible(3); + builder.add_nullifier_pending_read_non_revertible(1); + builder.succeeded(); + } + + #[test(should_fail_with="Hinted value does not match read request")] + unconstrained fn pending_nullifier_read_request_wrong_hint_fails() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + + builder.append_nullifiers(3); + builder.add_nullifier_pending_read(1); + let mut hint = builder.nullifier_read_request_reset_hints_builder.pending_read_hints.pop(); + hint.pending_value_index -= 1; + builder.nullifier_read_request_reset_hints_builder.pending_read_hints.push(hint); + + builder.failed(); + } + + #[test(should_fail_with="Read request counter must be greater than counter of the value being read")] + unconstrained fn pending_nullifier_read_request_reads_before_value_fails() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + + builder.append_nullifiers(3); + builder.add_nullifier_pending_read(1); + let nullifier_being_read = builder.previous_kernel.end.new_nullifiers.get(1); + let mut read_request = builder.previous_kernel.end.nullifier_read_requests.pop(); + read_request.counter = nullifier_being_read.counter - 1; + builder.previous_kernel.end.nullifier_read_requests.push(read_request); + + builder.failed(); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/Nargo.toml new file mode 100644 index 000000000000..56de9efcafb0 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "reset_kernel_lib" +type = "lib" +authors = [""] +compiler_version = ">=0.18.0" + +[dependencies] +types = { path = "../types" } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr new file mode 100644 index 000000000000..8e41dc16d8d6 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/lib.nr @@ -0,0 +1,6 @@ +use read_request_reset::reset_read_requests; +use nullifier_read_request_reset::NullifierReadRequestResetHints; +use nullifier_read_request_reset::NullifierReadRequestResetHintsBuilder; + +mod read_request_reset; +mod nullifier_read_request_reset; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/nullifier_read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_read_request_reset.nr similarity index 100% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/nullifier_read_request_reset.nr rename to noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_read_request_reset.nr diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/read_request_reset.nr similarity index 100% rename from noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/read_request_reset.nr rename to noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/read_request_reset.nr diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr index 6ea95afbd7e8..6db4386db9b3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr @@ -69,7 +69,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: true, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item request hash" test - assert_eq(call_stack_item.hash(), 0x2812dfeffdb7553fbbdd27c03fbdf61e3aa9bab3209db39f78838508ad892803); + assert_eq(call_stack_item.hash(), 0x24f9e91b4b000c5a07cdb371d0912bbe0d90de30f1757e8c19c92c8f11559e9d); } #[test] @@ -86,6 +86,6 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: false, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item hash" test - assert_eq(call_stack_item.hash(), 0x1f71c0d6bd03e409df694549b6aa83d706cfe55427152e6ec443ec64fa62d3a0); + assert_eq(call_stack_item.hash(), 0x0782bc16ebc3d668cc1e874c93dbd2ce52a8daa44c5fa5691db52007b226529a); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr index 0879063fc638..084105d534d7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr @@ -131,5 +131,5 @@ fn empty_hash() { let hash = inputs.hash(); // Value from public_circuit_public_inputs.test.ts "computes empty item hash" test - assert_eq(hash, 0x0d43290c164ebc3d80d4d17f1939482d9d01ad503cebceb8c665d2bd96597a68); + assert_eq(hash, 0x0f1eb4e352e8dab6cbab3c63b6d8f3cd2cd90cc7ae5ff142e4dfa2b3e28e01c1); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr index a4126dff9b77..caf18ac035c6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr @@ -134,9 +134,9 @@ impl PreviousKernelDataBuilder { } } - fn get_mock_nullifier_value(self, nullifier_index: u64) -> Field { - let first_nullifier = self.end.new_nullifiers.get(0); - first_nullifier.value + nullifier_index as Field + fn get_mock_nullifier_value(_self: Self, nullifier_index: u64) -> Field { + let value_offset = 5678; + value_offset + nullifier_index as Field } pub fn append_new_nullifiers_from_private(&mut self, num_extra_nullifier: u64) { @@ -160,11 +160,29 @@ impl PreviousKernelDataBuilder { pub fn append_new_nullifiers_from_public(&mut self, num_extra_nullifier: u64) { let index_offset = self.end.new_nullifiers.len(); - for i in 1..MAX_NEW_NULLIFIERS_PER_TX { - if i as u64 <= num_extra_nullifier as u64 { + for i in 0..MAX_NEW_NULLIFIERS_PER_TX { + if i as u64 < num_extra_nullifier as u64 { + let mock_value = self.get_mock_nullifier_value(index_offset + i); self.end.new_nullifiers.push( SideEffectLinkedToNoteHash { - value: self.get_mock_nullifier_value(index_offset + i), + value: silo_nullifier(self.storage_contract_address, mock_value), + note_hash: 0, + counter: self.next_sideffect_counter() + } + ); + } + } + } + + pub fn append_new_nullifiers_non_revertible_from_public(&mut self, num_extra_nullifier: u64) { + let index_offset = self.end_non_revertible.new_nullifiers.len(); + // Includes the tx nullifier at index 0. + for i in 0..MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX { + if i as u64 < num_extra_nullifier as u64 { + let mock_value = self.get_mock_nullifier_value(index_offset + i); + self.end.new_nullifiers.push( + SideEffectLinkedToNoteHash { + value: silo_nullifier(self.storage_contract_address, mock_value), note_hash: 0, counter: self.next_sideffect_counter() } @@ -175,8 +193,8 @@ impl PreviousKernelDataBuilder { pub fn append_new_nullifiers_non_revertible(&mut self, num_extra_nullifier: u64) { let index_offset = self.end_non_revertible.new_nullifiers.len(); - for i in 1..MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX { - if i as u64 <= num_extra_nullifier as u64 { + for i in 0..MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX { + if i as u64 < num_extra_nullifier as u64 { self.end_non_revertible.new_nullifiers.push( SideEffectLinkedToNoteHash { value: self.get_mock_nullifier_value(index_offset + i), @@ -200,6 +218,18 @@ impl PreviousKernelDataBuilder { read_request_index } + pub fn add_read_request_for_pending_nullifier_non_revertible(&mut self, nullifier_index: u64) -> u64 { + let read_request_index = self.end_non_revertible.nullifier_read_requests.len(); + let unsiloed_nullifier = self.get_mock_nullifier_value(nullifier_index); + let read_request = ReadRequestContext { + value: unsiloed_nullifier, + counter: self.next_sideffect_counter(), + contract_address: self.storage_contract_address + }; + self.end_non_revertible.nullifier_read_requests.push(read_request); + read_request_index + } + // snapshot the side effects // this is useful in the private tail circuit to test side effect splitting pub fn capture_min_revertible_side_effect_counter(&mut self) { 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 4cd8dd3e1969..83a68af714a0 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 @@ -83,6 +83,24 @@ pub fn array_concat(array1: [T; N], array2: [T; S]) -> [T; M] where result } +pub fn array_merge(array1: [T; N], array2: [T; N]) -> [T; N] where T: Empty + Eq { + let mut result: [T; N] = [T::empty(); N]; + let mut i = 0; + for elem in array1 { + if !is_empty(elem) { + result[i] = elem; + i += 1; + } + } + for elem in array2 { + if !is_empty(elem) { + result[i] = elem; + i += 1; + } + } + result +} + #[test] fn smoke_validate_array() { let valid_array = []; diff --git a/yarn-project/circuits.js/src/hints/build_hints.test.ts b/yarn-project/circuits.js/src/hints/build_hints.test.ts new file mode 100644 index 000000000000..6cf3572bbf77 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/build_hints.test.ts @@ -0,0 +1,120 @@ +import { + AztecAddress, + Fr, + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, + NullifierReadRequestResetHints, + NullifierReadRequestResetHintsBuilder, + PendingReadHint, + ReadRequestContext, + ReadRequestState, + ReadRequestStatus, + SettledReadHint, + SideEffectLinkedToNoteHash, +} from '@aztec/circuits.js'; +import { siloNullifier } from '@aztec/circuits.js/hash'; +import { makeTuple } from '@aztec/foundation/array'; +import { Tuple } from '@aztec/foundation/serialize'; + +import { HintsBuildingDataOracle, buildNullifierReadRequestResetHints } from './build_hints.js'; + +describe('buildNullifierReadRequestResetHints', () => { + const contractAddress = AztecAddress.random(); + const settledNullifierInnerValue = 99999; + const settledNullifierValue = makeNullifier(settledNullifierInnerValue).value; + const oracle: HintsBuildingDataOracle = { + getNullifierMembershipWitness: value => + value.equals(settledNullifierValue) ? ({ membershipWitness: {}, leafPreimage: {} } as any) : undefined, + }; + let nullifierReadRequests: Tuple; + let nullifiers: Tuple; + let expectedHints: NullifierReadRequestResetHints; + let numReadRequests = 0; + let numPendingReads = 0; + let numSettledReads = 0; + + const innerNullifier = (index: number) => index + 1; + + const makeReadRequest = (value: number, counter = 2) => + new ReadRequestContext(new Fr(value), counter, contractAddress); + + function makeNullifier(value: number, counter = 1) { + const siloedValue = siloNullifier(contractAddress, new Fr(value)); + return new SideEffectLinkedToNoteHash(siloedValue, new Fr(0), new Fr(counter)); + } + + const readPendingNullifier = ({ + nullifierIndex, + readRequestIndex = numReadRequests, + hintIndex = numPendingReads, + }: { + nullifierIndex: number; + readRequestIndex?: number; + hintIndex?: number; + }) => { + nullifierReadRequests[readRequestIndex] = makeReadRequest(innerNullifier(nullifierIndex)); + expectedHints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus(ReadRequestState.PENDING, hintIndex); + expectedHints.pendingReadHints[hintIndex] = new PendingReadHint(readRequestIndex, nullifierIndex); + numReadRequests++; + numPendingReads++; + }; + + const readSettledNullifier = ({ + readRequestIndex = numReadRequests, + hintIndex = numSettledReads, + }: { + readRequestIndex?: number; + hintIndex?: number; + } = {}) => { + nullifierReadRequests[readRequestIndex] = makeReadRequest(settledNullifierInnerValue); + expectedHints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus(ReadRequestState.SETTLED, hintIndex); + expectedHints.settledReadHints[hintIndex] = new SettledReadHint(readRequestIndex, {} as any, {} as any); + numReadRequests++; + numSettledReads++; + }; + + const buildHints = () => buildNullifierReadRequestResetHints(oracle, nullifierReadRequests, nullifiers); + + beforeEach(() => { + nullifierReadRequests = makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ReadRequestContext.empty); + nullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, i => makeNullifier(innerNullifier(i))); + expectedHints = NullifierReadRequestResetHintsBuilder.empty(); + numReadRequests = 0; + numPendingReads = 0; + numSettledReads = 0; + }); + + it('builds empty hints', async () => { + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('builds hints for pending nullifier read requests', async () => { + readPendingNullifier({ nullifierIndex: 2 }); + readPendingNullifier({ nullifierIndex: 1 }); + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('builds hints for settled nullifier read requests', async () => { + readSettledNullifier(); + readSettledNullifier(); + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('builds hints for mixed pending and settled nullifier read requests', async () => { + readPendingNullifier({ nullifierIndex: 2 }); + readSettledNullifier(); + readSettledNullifier(); + readPendingNullifier({ nullifierIndex: 1 }); + readPendingNullifier({ nullifierIndex: 1 }); + const hints = await buildHints(); + expect(hints).toEqual(expectedHints); + }); + + it('throws if reading an unknown nullifier', async () => { + nullifierReadRequests[0] = makeReadRequest(88888); + await expect(buildHints()).rejects.toThrow('Read request is reading an unknown nullifier value.'); + }); +}); diff --git a/yarn-project/circuits.js/src/hints/build_hints.ts b/yarn-project/circuits.js/src/hints/build_hints.ts new file mode 100644 index 000000000000..d80f4bd5c375 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/build_hints.ts @@ -0,0 +1,60 @@ +import { Fr } from '@aztec/foundation/fields'; +import { Tuple } from '@aztec/foundation/serialize'; + +import { + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, + NULLIFIER_TREE_HEIGHT, +} from '../constants.gen.js'; +import { siloNullifier } from '../hash/index.js'; +import { MembershipWitness } from '../structs/membership_witness.js'; +import { ReadRequestContext } from '../structs/read_request.js'; +import { NullifierReadRequestResetHintsBuilder } from '../structs/read_request_reset_hints.js'; +import { NullifierLeafPreimage } from '../structs/rollup/nullifier_leaf/index.js'; +import { SideEffectLinkedToNoteHash } from '../structs/side_effects.js'; +import { countAccumulatedItems } from './utils.js'; + +export interface NullifierMembershipWitnessWithPreimage { + membershipWitness: MembershipWitness; + leafPreimage: NullifierLeafPreimage; +} + +export interface HintsBuildingDataOracle { + getNullifierMembershipWitness(nullifier: Fr): Promise; +} + +export async function buildNullifierReadRequestResetHints( + oracle: HintsBuildingDataOracle, + nullifierReadRequests: Tuple, + nullifiers: Tuple, +) { + const builder = new NullifierReadRequestResetHintsBuilder(); + + const numReadRequests = countAccumulatedItems(nullifierReadRequests); + + const nullifierIndexMap: Map = new Map(); + nullifiers.forEach((n, i) => nullifierIndexMap.set(n.value.toBigInt(), i)); + + for (let i = 0; i < numReadRequests; ++i) { + const readRequest = nullifierReadRequests[i]; + // TODO - Should be comparing un-siloed values and contract addresses. + const value = siloNullifier(readRequest.contractAddress, readRequest.value); + + const pendingValueIndex = nullifierIndexMap.get(value.toBigInt()); + if (pendingValueIndex !== undefined) { + builder.addPendingReadRequest(i, pendingValueIndex); + } else { + const membershipWitnessWithPreimage = await oracle.getNullifierMembershipWitness(value); + if (!membershipWitnessWithPreimage) { + throw new Error('Read request is reading an unknown nullifier value.'); + } + + builder.addSettledReadRequest( + i, + membershipWitnessWithPreimage.membershipWitness, + membershipWitnessWithPreimage.leafPreimage, + ); + } + } + return builder.toHints(); +} diff --git a/yarn-project/circuits.js/src/hints/index.ts b/yarn-project/circuits.js/src/hints/index.ts new file mode 100644 index 000000000000..ef50cf70092d --- /dev/null +++ b/yarn-project/circuits.js/src/hints/index.ts @@ -0,0 +1,2 @@ +export * from './build_hints.js'; +export * from './utils.js'; diff --git a/yarn-project/circuits.js/src/hints/utils.test.ts b/yarn-project/circuits.js/src/hints/utils.test.ts new file mode 100644 index 000000000000..bee011ed0127 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/utils.test.ts @@ -0,0 +1,160 @@ +import { IsEmpty } from '@aztec/circuits.js'; +import { makeTuple } from '@aztec/foundation/array'; +import { Tuple } from '@aztec/foundation/serialize'; + +import { concatAccumulatedData, countAccumulatedItems, mergeAccumulatedData } from './utils.js'; + +class TestItem { + constructor(public value: number) {} + + static empty() { + return new TestItem(0); + } + + isEmpty() { + return this.value === 0; + } +} + +describe('hints utils', () => { + const expectEmptyArrays = (arr: IsEmpty[]) => { + arr.forEach(item => expect(item.isEmpty()).toBe(true)); + }; + + describe('countAccumulatedItems', () => { + it('counts the number of non-empty items', () => { + const arr = makeTuple(20, TestItem.empty); + const num = 6; + for (let i = 0; i < num; ++i) { + arr[i] = new TestItem(i + 1); + } + expect(countAccumulatedItems(arr)).toBe(num); + }); + + it('throws if arr contains non-continuous non-empty items', () => { + const arr = makeTuple(20, TestItem.empty); + arr[1] = new TestItem(123); + expect(() => countAccumulatedItems(arr)).toThrow('Non-empty items must be placed continuously from index 0.'); + }); + }); + + describe('mergeAccumulatedData', () => { + const length = 5; + let arr0: Tuple; + let arr1: Tuple; + + beforeEach(() => { + arr0 = makeTuple(length, TestItem.empty); + arr1 = makeTuple(length, TestItem.empty); + }); + + it('propagates items from arr0', () => { + arr0[0] = new TestItem(12); + arr0[1] = new TestItem(34); + const res = mergeAccumulatedData(length, arr0, arr1); + expect(res.slice(0, 2)).toEqual([arr0[0], arr0[1]]); + expectEmptyArrays(res.slice(2)); + }); + + it('propagates items from arr1', () => { + arr1[0] = new TestItem(1); + arr1[1] = new TestItem(2); + const res = mergeAccumulatedData(length, arr0, arr1); + expect(res.slice(0, 2)).toEqual([arr1[0], arr1[1]]); + expectEmptyArrays(res.slice(2)); + }); + + it('merges items from both arrays', () => { + arr0[0] = new TestItem(12); + arr0[1] = new TestItem(34); + arr1[0] = new TestItem(1); + arr1[1] = new TestItem(2); + const res = mergeAccumulatedData(length, arr0, arr1); + expect(res.slice(0, 4)).toEqual([arr0[0], arr0[1], arr1[0], arr1[1]]); + expectEmptyArrays(res.slice(4)); + }); + + it('throws if arr0 contains non-continuous items', () => { + arr0[0] = new TestItem(12); + arr0[2] = new TestItem(34); + expect(() => mergeAccumulatedData(length, arr0, arr1)).toThrow( + 'Non-empty items must be placed continuously from index 0.', + ); + }); + + it('throws if arr1 contains non-continuous items', () => { + arr1[0] = new TestItem(12); + arr1[2] = new TestItem(34); + expect(() => mergeAccumulatedData(length, arr0, arr1)).toThrow( + 'Non-empty items must be placed continuously from index 0.', + ); + }); + + it('throws if total number of items exceeds limit', () => { + for (let i = 0; i < length; ++i) { + arr0[i] = new TestItem(i + 1); + } + expect(mergeAccumulatedData(length, arr0, arr1)).toBeDefined(); + + arr1[0] = new TestItem(1234); + expect(() => mergeAccumulatedData(length, arr0, arr1)).toThrow( + 'Combined non-empty items exceeded the maximum allowed.', + ); + }); + }); + + describe('concatAccumulatedData', () => { + const length0 = 3; + const length1 = 5; + const length = length0 + length1; + let arr0: Tuple; + let arr1: Tuple; + + beforeEach(() => { + arr0 = makeTuple(length0, TestItem.empty); + arr1 = makeTuple(length1, TestItem.empty); + }); + + it('propagates items from arr0', () => { + arr0[0] = new TestItem(12); + arr0[1] = new TestItem(34); + const nullifiers = concatAccumulatedData(length, arr0, arr1); + expect(nullifiers.slice(0, 2)).toEqual([arr0[0], arr0[1]]); + expectEmptyArrays(nullifiers.slice(2)); + }); + + it('propagates items from arr1', () => { + arr1[0] = new TestItem(1); + arr1[1] = new TestItem(2); + const nullifiers = concatAccumulatedData(length, arr0, arr1); + expect(nullifiers.slice(0, 2)).toEqual([arr1[0], arr1[1]]); + expectEmptyArrays(nullifiers.slice(2)); + }); + + it('combines items from both arrays', () => { + arr0[0] = new TestItem(12); + arr0[1] = new TestItem(34); + arr1[0] = new TestItem(1); + arr1[1] = new TestItem(2); + const nullifiers = concatAccumulatedData(length, arr0, arr1); + expect(nullifiers.slice(0, 4)).toEqual([arr0[0], arr0[1], arr1[0], arr1[1]]); + expectEmptyArrays(nullifiers.slice(4)); + }); + + it('throws if arr0 contains non-continuous items', () => { + arr0[0] = new TestItem(12); + arr0[2] = new TestItem(34); + expect(() => concatAccumulatedData(length, arr0, arr1)).toThrow( + 'Non-empty items must be placed continuously from index 0.', + ); + }); + + it('throws if arr1 contains non-continuous items', () => { + arr1[0] = new TestItem(12); + arr1[2] = new TestItem(34); + expect(() => concatAccumulatedData(length, arr0, arr1)).toThrow( + 'Non-empty items must be placed continuously from index 0.', + ); + }); + }); +}); diff --git a/yarn-project/circuits.js/src/hints/utils.ts b/yarn-project/circuits.js/src/hints/utils.ts new file mode 100644 index 000000000000..a228844c8a30 --- /dev/null +++ b/yarn-project/circuits.js/src/hints/utils.ts @@ -0,0 +1,60 @@ +import { Tuple } from '@aztec/foundation/serialize'; + +export interface IsEmpty { + isEmpty: () => boolean; +} + +// Define these utils here as their design is very specific to kernel's accumulated data and not general enough to be put in foundation. + +// Returns number of non-empty items in an array. +export function countAccumulatedItems(arr: T[]) { + return arr.reduce((num, item, i) => { + if (!item.isEmpty()) { + if (num !== i) { + throw new Error('Non-empty items must be placed continuously from index 0.'); + } + return num + 1; + } + return num; + }, 0); +} + +// Merges two arrays of length N into an array of length N. +export function mergeAccumulatedData( + _length: N, + arr0: Tuple, + arr1: Tuple, +): Tuple { + const numNonEmptyItems0 = countAccumulatedItems(arr0); + const numNonEmptyItems1 = countAccumulatedItems(arr1); + if (numNonEmptyItems0 + numNonEmptyItems1 > arr0.length) { + throw new Error('Combined non-empty items exceeded the maximum allowed.'); + } + + const arr = [...arr0] as Tuple; + arr1.slice(0, numNonEmptyItems1).forEach((item, i) => (arr[i + numNonEmptyItems0] = item)); + return arr; +} + +// Combines an array of length N and an array of length M into an array of length N + M. +// All non-empty items are aggregated continuously from index 0. +export function concatAccumulatedData( + length: NM, + arr0: Tuple, + arr1: Tuple, +): Tuple { + const combinedLength = arr0.length + arr1.length; + if (combinedLength !== length) { + throw new Error(`Provided length does not match combined length. Expected ${combinedLength}. Got ${length}.`); + } + + const numNonEmptyItems0 = countAccumulatedItems(arr0); + const numNonEmptyItems1 = countAccumulatedItems(arr1); + const emptyItem = arr0[numNonEmptyItems0] || arr1[numNonEmptyItems1]; + const arr = [...arr0, ...arr1] as Tuple; + arr1.slice(0, numNonEmptyItems1).forEach((item, i) => { + arr[i + numNonEmptyItems0] = item; + arr[arr0.length + i] = emptyItem; + }); + return arr; +} diff --git a/yarn-project/circuits.js/src/index.ts b/yarn-project/circuits.js/src/index.ts index 8ba7029a564a..1fafc4e3bf56 100644 --- a/yarn-project/circuits.js/src/index.ts +++ b/yarn-project/circuits.js/src/index.ts @@ -1,5 +1,6 @@ export * from './constants.gen.js'; export * from './contract/index.js'; +export * from './hints/index.js'; export * from './keys/index.js'; export * from './structs/index.js'; export * from './types/index.js'; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap index 735f7ea94fd6..4e7c27701b9a 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap @@ -1,46 +1,46 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x1f71c0d6bd03e409df694549b6aa83d706cfe55427152e6ec443ec64fa62d3a0"`; +exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x0782bc16ebc3d668cc1e874c93dbd2ce52a8daa44c5fa5691db52007b226529a"`; -exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x2812dfeffdb7553fbbdd27c03fbdf61e3aa9bab3209db39f78838508ad892803"`; +exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x24f9e91b4b000c5a07cdb371d0912bbe0d90de30f1757e8c19c92c8f11559e9d"`; exports[`PublicCallStackItem computes hash 1`] = ` Fr { - "asBigInt": 5564031247356020353810998760199482863796790393190610135965142067359455090525n, + "asBigInt": 3860793879659362473516834500228100554565219506438775723714730590510147022386n, "asBuffer": { "data": [ - 12, - 77, - 33, - 77, - 64, - 203, + 8, + 137, 33, - 253, - 25, - 21, - 66, - 225, + 196, + 12, + 184, + 100, + 73, + 63, + 103, + 24, + 91, + 202, + 177, + 49, + 17, + 110, + 174, + 165, + 58, + 6, + 1, 22, - 94, - 25, - 36, - 156, - 159, - 227, - 54, - 40, - 115, - 108, - 155, - 82, - 234, - 144, - 55, - 50, - 211, - 151, + 242, + 180, + 178, + 168, + 143, + 43, 93, + 174, + 50, ], "type": "Buffer", }, diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap index 87eca1e4dd22..5491e54e2c4a 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap @@ -2,41 +2,41 @@ exports[`PublicCircuitPublicInputs computes empty item hash 1`] = ` Fr { - "asBigInt": 5998729082391453463848740365500417056769011535294882115251337368669961943656n, + "asBigInt": 6838946583674061741230763190496060139694703525620604506619781596976528622017n, "asBuffer": { "data": [ - 13, - 67, - 41, + 15, + 30, + 180, + 227, + 82, + 232, + 218, + 182, + 203, + 171, + 60, + 99, + 182, + 216, + 243, + 205, + 44, + 217, 12, - 22, - 78, - 188, - 61, - 128, - 212, - 209, - 127, - 25, - 57, - 72, - 45, - 157, + 199, + 174, + 95, + 241, + 66, + 228, + 223, + 162, + 179, + 226, + 142, 1, - 173, - 80, - 60, - 235, - 206, - 184, - 198, - 101, - 210, - 189, - 150, - 89, - 122, - 104, + 193, ], "type": "Buffer", }, @@ -45,41 +45,41 @@ Fr { exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = ` Fr { - "asBigInt": 888846281962498247810067532853731271075966629879817122598695403485138176580n, + "asBigInt": 7221046481316377884330035139997978480007247696771040776655314520338551306922n, "asBuffer": { "data": [ - 1, + 15, + 246, 247, - 17, - 180, - 245, - 241, - 13, - 223, - 202, - 143, - 57, - 70, - 74, - 0, - 106, - 174, - 207, - 10, - 115, - 35, - 110, - 39, + 172, + 58, + 158, + 189, + 228, + 213, 45, - 164, - 201, - 195, - 236, - 184, - 208, - 18, - 202, - 68, + 54, + 170, + 146, + 50, + 28, + 231, + 7, + 2, + 198, + 75, + 193, + 95, + 255, + 36, + 90, + 56, + 53, + 140, + 219, + 216, + 122, + 170, ], "type": "Buffer", }, diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts index 9791da9f4804..b107b9c9714f 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts @@ -1,6 +1,7 @@ import { serializeToBuffer } from '@aztec/foundation/serialize'; import { PublicKernelData } from './public_kernel_data.js'; +import { NullifierReadRequestResetHints } from '../read_request_reset_hints.js'; /** * Inputs to the public kernel circuit. @@ -11,9 +12,13 @@ export class PublicKernelTailCircuitPrivateInputs { * Kernels are recursive and this is the data from the previous kernel. */ public readonly previousKernel: PublicKernelData, + /** + * Contains hints for the nullifier read requests to locate corresponding pending or settled nullifiers. + */ + public nullifierReadRequestResetHints: NullifierReadRequestResetHints, ) {} toBuffer() { - return serializeToBuffer(this.previousKernel); + return serializeToBuffer(this.previousKernel, this.nullifierReadRequestResetHints); } } diff --git a/yarn-project/circuits.js/src/structs/read_request_reset_hints.ts b/yarn-project/circuits.js/src/structs/read_request_reset_hints.ts index d3f04ce89dbd..ef43419940a4 100644 --- a/yarn-project/circuits.js/src/structs/read_request_reset_hints.ts +++ b/yarn-project/circuits.js/src/structs/read_request_reset_hints.ts @@ -174,6 +174,10 @@ export class NullifierReadRequestResetHintsBuilder { ); } + static empty() { + return new NullifierReadRequestResetHintsBuilder().toHints(); + } + addPendingReadRequest(readRequestIndex: number, nullifierIndex: number) { this.hints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus( ReadRequestState.PENDING, @@ -189,7 +193,7 @@ export class NullifierReadRequestResetHintsBuilder { leafPreimage: NullifierLeafPreimage, ) { this.hints.readRequestStatuses[readRequestIndex] = new ReadRequestStatus( - ReadRequestState.PENDING, + ReadRequestState.SETTLED, this.numSettledReadHints, ); this.hints.settledReadHints[this.numSettledReadHints] = new SettledReadHint( 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 b36e2409744b..e2c5844e0ef8 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -1407,6 +1407,7 @@ export function mapPublicKernelTailCircuitPrivateInputsToNoir( ): PublicKernelTailCircuitPrivateInputsNoir { return { previous_kernel: mapPublicKernelDataToNoir(inputs.previousKernel), + nullifier_read_request_reset_hints: mapNullifierReadRequestResetHintsToNoir(inputs.nullifierReadRequestResetHints), }; } diff --git a/yarn-project/pxe/src/kernel_oracle/index.ts b/yarn-project/pxe/src/kernel_oracle/index.ts index ae5804d86d6d..d37b84338cd3 100644 --- a/yarn-project/pxe/src/kernel_oracle/index.ts +++ b/yarn-project/pxe/src/kernel_oracle/index.ts @@ -14,6 +14,8 @@ import { Tuple } from '@aztec/foundation/serialize'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { ProvingDataOracle } from './../kernel_prover/proving_data_oracle.js'; +// TODO: Block number should not be "latest". +// It should be fixed at the time the proof is being simulated. I.e., it should be the same as the value defined in the constant data. /** * A data oracle that provides information needed for simulating a transaction. */ @@ -50,8 +52,8 @@ export class KernelOracle implements ProvingDataOracle { ); } - getNullifierMembershipWitness(blockNumber: number, nullifier: Fr) { - return this.node.getNullifierMembershipWitness(blockNumber, nullifier); + getNullifierMembershipWitness(nullifier: Fr) { + return this.node.getNullifierMembershipWitness('latest', nullifier); } async getNoteHashTreeRoot(): Promise { diff --git a/yarn-project/pxe/src/kernel_prover/hints_builder.ts b/yarn-project/pxe/src/kernel_prover/hints_builder.ts index 5ebd2abe738d..7f5dfd881e7b 100644 --- a/yarn-project/pxe/src/kernel_prover/hints_builder.ts +++ b/yarn-project/pxe/src/kernel_prover/hints_builder.ts @@ -9,13 +9,12 @@ import { MembershipWitness, NULLIFIER_TREE_HEIGHT, NullifierKeyValidationRequestContext, - NullifierReadRequestResetHintsBuilder, ReadRequestContext, SideEffect, SideEffectLinkedToNoteHash, SideEffectType, + buildNullifierReadRequestResetHints, } from '@aztec/circuits.js'; -import { siloNullifier } from '@aztec/circuits.js/hash'; import { makeTuple } from '@aztec/foundation/array'; import { Tuple } from '@aztec/foundation/serialize'; @@ -75,42 +74,28 @@ export class HintsBuilder { return hints; } - async getNullifierReadRequestResetHints( + getNullifierReadRequestResetHints( nullifierReadRequests: Tuple, nullifiers: Tuple, ) { - // TODO - Should be comparing un-siloed values and contract addresses. - const builder = new NullifierReadRequestResetHintsBuilder(); - const nullifierIndexMap: Map = new Map(); - nullifiers.forEach((n, i) => nullifierIndexMap.set(n.value.toBigInt(), i)); - const siloedReadRequestValues = nullifierReadRequests.map(r => - r.isEmpty() ? Fr.ZERO : siloNullifier(r.contractAddress, r.value), - ); - for (let i = 0; i < nullifierReadRequests.length; ++i) { - const value = siloedReadRequestValues[i]; - if (value.isZero()) { - break; - } - const pendingValueIndex = nullifierIndexMap.get(value.toBigInt()); - if (pendingValueIndex !== undefined) { - builder.addPendingReadRequest(i, pendingValueIndex); - } else { - const membershipWitness = await this.oracle.getNullifierMembershipWitness(0, value); - if (!membershipWitness) { - throw new Error('Read request is reading an unknown nullifier value.'); - } - builder.addSettledReadRequest( - i, - new MembershipWitness( - NULLIFIER_TREE_HEIGHT, - membershipWitness.index, - membershipWitness.siblingPath.toTuple(), - ), - membershipWitness.leafPreimage, - ); - } + return buildNullifierReadRequestResetHints(this, nullifierReadRequests, nullifiers); + } + + async getNullifierMembershipWitness(nullifier: Fr) { + const res = await this.oracle.getNullifierMembershipWitness(nullifier); + if (!res) { + return; } - return builder.toHints(); + + const { index, siblingPath, leafPreimage } = res; + return { + membershipWitness: new MembershipWitness( + NULLIFIER_TREE_HEIGHT, + index, + siblingPath.toTuple(), + ), + leafPreimage, + }; } /** diff --git a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts index b9685a7aa6a8..2126f70e38d3 100644 --- a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts +++ b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts @@ -60,7 +60,7 @@ export interface ProvingDataOracle { */ getNoteMembershipWitness(leafIndex: bigint): Promise>; - getNullifierMembershipWitness(blockNumber: number, nullifier: Fr): Promise; + getNullifierMembershipWitness(nullifier: Fr): Promise; /** * Get the root of the note hash tree. diff --git a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts index 2daaa2cccc94..83a926a05be2 100644 --- a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts +++ b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts @@ -59,6 +59,7 @@ import { env } from 'process'; import { getVerificationKeys } from '../mocks/verification_keys.js'; import { PublicProver } from '../prover/index.js'; import { PublicKernelCircuitSimulator } from '../simulator/index.js'; +import { HintsBuilder } from './hints_builder.js'; import { FailedTx } from './processed_tx.js'; export enum PublicKernelPhase { @@ -76,6 +77,7 @@ export const PhaseIsRevertible: Record = { }; export abstract class AbstractPhaseManager { + protected hintsBuilder: HintsBuilder; protected log: DebugLogger; constructor( protected db: MerkleTreeOperations, @@ -86,6 +88,7 @@ export abstract class AbstractPhaseManager { protected historicalHeader: Header, public phase: PublicKernelPhase, ) { + this.hintsBuilder = new HintsBuilder(db); this.log = createDebugLogger(`aztec:sequencer:${phase}`); } /** @@ -249,7 +252,7 @@ export abstract class AbstractPhaseManager { executionStack.push(...result.nestedExecutions); const callData = await this.getPublicCallData(result, isExecutionRequest); - [kernelOutput, kernelProof] = await this.runKernelCircuitWithCallData(callData, kernelOutput, kernelProof); + [kernelOutput, kernelProof] = await this.runKernelCircuit(kernelOutput, kernelProof, callData); if (!enqueuedExecutionResult) { enqueuedExecutionResult = result; @@ -266,26 +269,17 @@ export abstract class AbstractPhaseManager { return [kernelOutput, kernelProof, newUnencryptedFunctionLogs]; } - public async runKernelCircuit( - previousOutput: PublicKernelCircuitPublicInputs, - previousProof: Proof, - ): Promise<[PublicKernelCircuitPublicInputs, Proof]> { - const output = await this.getKernelCircuitOutput(previousOutput, previousProof); - const proof = await this.publicProver.getPublicKernelCircuitProof(output); - return [output, proof]; - } - - protected async runKernelCircuitWithCallData( - callData: PublicCallData, + protected async runKernelCircuit( previousOutput: PublicKernelCircuitPublicInputs, previousProof: Proof, + callData?: PublicCallData, ): Promise<[PublicKernelCircuitPublicInputs, Proof]> { const output = await this.getKernelCircuitOutput(previousOutput, previousProof, callData); const proof = await this.publicProver.getPublicKernelCircuitProof(output); return [output, proof]; } - protected getKernelCircuitOutput( + protected async getKernelCircuitOutput( previousOutput: PublicKernelCircuitPublicInputs, previousProof: Proof, callData?: PublicCallData, @@ -293,7 +287,14 @@ export abstract class AbstractPhaseManager { const previousKernel = this.getPreviousKernelData(previousOutput, previousProof); if (this.phase === PublicKernelPhase.TAIL) { - const inputs = new PublicKernelTailCircuitPrivateInputs(previousKernel); + const { endNonRevertibleData, end } = previousOutput; + const nullifierReadRequestResetHints = await this.hintsBuilder.getNullifierReadRequestResetHints( + endNonRevertibleData.nullifierReadRequests, + end.nullifierReadRequests, + endNonRevertibleData.newNullifiers, + end.newNullifiers, + ); + const inputs = new PublicKernelTailCircuitPrivateInputs(previousKernel, nullifierReadRequestResetHints); return this.publicKernel.publicKernelCircuitTail(inputs); } diff --git a/yarn-project/sequencer-client/src/sequencer/hints_builder.ts b/yarn-project/sequencer-client/src/sequencer/hints_builder.ts new file mode 100644 index 000000000000..d7abe9fa0d95 --- /dev/null +++ b/yarn-project/sequencer-client/src/sequencer/hints_builder.ts @@ -0,0 +1,56 @@ +import { MerkleTreeId } from '@aztec/circuit-types'; +import { + Fr, + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, + MAX_REVERTIBLE_NULLIFIERS_PER_TX, + MembershipWitness, + NULLIFIER_TREE_HEIGHT, + NullifierLeafPreimage, + ReadRequestContext, + SideEffectLinkedToNoteHash, + buildNullifierReadRequestResetHints, + concatAccumulatedData, + mergeAccumulatedData, +} from '@aztec/circuits.js'; +import { Tuple } from '@aztec/foundation/serialize'; +import { MerkleTreeOperations } from '@aztec/world-state'; + +export class HintsBuilder { + constructor(private db: MerkleTreeOperations) {} + + getNullifierReadRequestResetHints( + nullifierReadRequestsNonRevertible: Tuple, + nullifierReadRequestsRevertible: Tuple, + nullifiersNonRevertible: Tuple, + nullifiersRevertible: Tuple, + ) { + return buildNullifierReadRequestResetHints( + this, + mergeAccumulatedData( + MAX_NULLIFIER_READ_REQUESTS_PER_TX, + nullifierReadRequestsNonRevertible, + nullifierReadRequestsRevertible, + ), + concatAccumulatedData(MAX_NEW_NULLIFIERS_PER_TX, nullifiersNonRevertible, nullifiersRevertible), + ); + } + + async getNullifierMembershipWitness(nullifier: Fr) { + const index = await this.db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + if (index === undefined) { + return; + } + + const siblingPath = await this.db.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, index); + const membershipWitness = new MembershipWitness( + NULLIFIER_TREE_HEIGHT, + index, + siblingPath.toTuple(), + ); + const leafPreimage = (await this.db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))! as NullifierLeafPreimage; + + return { membershipWitness, leafPreimage }; + } +} From fd6f08eeef2bcf0f4a566dbe8dfa76412de8c425 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 6 Mar 2024 19:05:43 +0000 Subject: [PATCH 04/10] Fix package name. --- .../crates/public-kernel-tail-simulated/Nargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml index 698657b684db..57448d5086f0 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-tail-simulated/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "public_kernel_teardown_simulated" +name = "public_kernel_tail_simulated" type = "bin" authors = [""] compiler_version = ">=0.18.0" From 6a7417f4c12bcaecfa7da72265b8fcfeb0a7ae45 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 6 Mar 2024 19:18:37 +0000 Subject: [PATCH 05/10] Fix. --- .../structs/kernel/public_kernel_tail_circuit_private_inputs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts index b107b9c9714f..37d79805d119 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_tail_circuit_private_inputs.ts @@ -1,7 +1,7 @@ import { serializeToBuffer } from '@aztec/foundation/serialize'; -import { PublicKernelData } from './public_kernel_data.js'; import { NullifierReadRequestResetHints } from '../read_request_reset_hints.js'; +import { PublicKernelData } from './public_kernel_data.js'; /** * Inputs to the public kernel circuit. From 81b09779059f333ddb3fb01fa47405dd2d43b133 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 6 Mar 2024 20:51:05 +0000 Subject: [PATCH 06/10] Fixes. --- .../public-kernel-lib/src/public_kernel_tail.nr | 5 ++--- .../crates/types/src/utils/arrays.nr | 1 + yarn-project/circuits.js/src/hints/utils.test.ts | 13 +++++++++++++ yarn-project/circuits.js/src/hints/utils.ts | 10 ++++++---- .../src/structs/kernel/combined_accumulated_data.ts | 9 ++++++++- .../src/type_conversion.ts | 4 ++-- .../src/sequencer/public_processor.test.ts | 6 +++--- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index ecb56535263c..1d7be12c5ea0 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -5,8 +5,7 @@ use dep::types::{ kernel_circuit_public_inputs::{PublicKernelCircuitPublicInputs, PublicKernelCircuitPublicInputsBuilder}, kernel_data::PublicKernelData, side_effect::SideEffectLinkedToNoteHash }, - constants::MAX_NULLIFIER_READ_REQUESTS_PER_TX, - utils::{arrays::{array_length, array_merge, array_concat}} + constants::MAX_NEW_NULLIFIERS_PER_TX, utils::{arrays::{array_length, array_merge, array_concat}} }; use dep::std::unsafe; @@ -39,7 +38,7 @@ impl PublicKernelTailCircuitPrivateInputs { end.nullifier_read_requests ); - let pending_nullifiers: [SideEffectLinkedToNoteHash; MAX_NULLIFIER_READ_REQUESTS_PER_TX] = array_concat(end_non_revertible.new_nullifiers, end.new_nullifiers); + let pending_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX] = array_concat(end_non_revertible.new_nullifiers, end.new_nullifiers); let hints = self.nullifier_read_request_reset_hints; 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 47c7567df597..ec5e186d412b 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 @@ -66,6 +66,7 @@ pub fn array_cp(array: [T; N]) -> [T; S] where T: Empty { } pub fn array_concat(array1: [T; N], array2: [T; S]) -> [T; M] where T: Empty + Eq { + assert(N + S <= M, "combined array length exceeds length of return array"); let mut result: [T; M] = [T::empty(); M]; let mut i = 0; for elem in array1 { diff --git a/yarn-project/circuits.js/src/hints/utils.test.ts b/yarn-project/circuits.js/src/hints/utils.test.ts index bee011ed0127..e5897e30a87f 100644 --- a/yarn-project/circuits.js/src/hints/utils.test.ts +++ b/yarn-project/circuits.js/src/hints/utils.test.ts @@ -141,6 +141,19 @@ describe('hints utils', () => { expectEmptyArrays(nullifiers.slice(4)); }); + it('combines all items from both arrays', () => { + arr0 = makeTuple(length0, i => new TestItem(i + 1)); + arr1 = makeTuple(length1, i => new TestItem(i + 999)); + const nullifiers = concatAccumulatedData(length, arr0, arr1); + expect(nullifiers).toEqual([...arr0, ...arr1]); + }); + + it('throws if given length is incorrect', () => { + expect(() => concatAccumulatedData(length + 1, arr0, arr1)).toThrow( + /Provided length does not match combined length./, + ); + }); + it('throws if arr0 contains non-continuous items', () => { arr0[0] = new TestItem(12); arr0[2] = new TestItem(34); diff --git a/yarn-project/circuits.js/src/hints/utils.ts b/yarn-project/circuits.js/src/hints/utils.ts index a228844c8a30..53f6ad73b6fc 100644 --- a/yarn-project/circuits.js/src/hints/utils.ts +++ b/yarn-project/circuits.js/src/hints/utils.ts @@ -52,9 +52,11 @@ export function concatAccumulatedData; - arr1.slice(0, numNonEmptyItems1).forEach((item, i) => { - arr[i + numNonEmptyItems0] = item; - arr[arr0.length + i] = emptyItem; - }); + if (numNonEmptyItems0 < arr0.length) { + arr1.slice(0, numNonEmptyItems1).forEach((item, i) => { + arr[i + numNonEmptyItems0] = item; + arr[arr0.length + i] = emptyItem; + }); + } return arr; } diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index aabc5088e1cc..f00fdc7e1dda 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -744,7 +744,14 @@ export class PublicAccumulatedNonRevertibleData { ) {} toBuffer() { - return serializeToBuffer(this.newNoteHashes, this.newNullifiers, this.publicCallStack); + return serializeToBuffer( + this.nullifierReadRequests, + this.newNoteHashes, + this.newNullifiers, + this.publicCallStack, + this.publicDataUpdateRequests, + this.publicDataReads, + ); } static fromBuffer(buffer: Buffer | BufferReader) { 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 6ee998946b80..321ca0a5d28c 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -725,7 +725,7 @@ export function mapPrivateCircuitPublicInputsToNoir( args_hash: mapFieldToNoir(privateCircuitPublicInputs.argsHash), return_values: mapTuple(privateCircuitPublicInputs.returnValues, mapFieldToNoir), note_hash_read_requests: mapTuple(privateCircuitPublicInputs.noteHashReadRequests, mapSideEffectToNoir), - nullifier_read_requests: mapTuple(privateCircuitPublicInputs.nullifierReadRequests, mapSideEffectToNoir), + nullifier_read_requests: mapTuple(privateCircuitPublicInputs.nullifierReadRequests, mapReadRequestToNoir), nullifier_key_validation_requests: mapTuple( privateCircuitPublicInputs.nullifierKeyValidationRequests, mapNullifierKeyValidationRequestToNoir, @@ -1233,7 +1233,7 @@ export function mapPublicAccumulatedNonRevertibleDataToNoir( data: PublicAccumulatedNonRevertibleData, ): PublicAccumulatedNonRevertibleDataNoir { return { - nullifier_read_requests: mapTuple(data.nullifierReadRequests, mapSideEffectToNoir), + nullifier_read_requests: mapTuple(data.nullifierReadRequests, mapReadRequestContextToNoir), new_note_hashes: mapTuple(data.newNoteHashes, mapSideEffectToNoir), new_nullifiers: mapTuple(data.newNullifiers, mapSideEffectLinkedToNoir), public_call_stack: mapTuple(data.publicCallStack, mapCallRequestToNoir), diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index f6a802d1b81f..d03c7e8fd533 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -225,7 +225,7 @@ describe('public_processor', () => { expect(processed).toEqual([expectedTxByHash(tx)]); expect(failed).toHaveLength(0); expect(publicExecutor.simulate).toHaveBeenCalledTimes(2); - expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(1); + expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(2); expect(publicWorldStateDB.rollback).toHaveBeenCalledTimes(0); }); @@ -273,7 +273,7 @@ describe('public_processor', () => { expect(processed).toEqual([expectedTxByHash(tx)]); expect(failed).toHaveLength(0); expect(publicExecutor.simulate).toHaveBeenCalledTimes(1); - expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(1); + expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(2); expect(publicWorldStateDB.rollback).toHaveBeenCalledTimes(0); }); @@ -439,7 +439,7 @@ describe('public_processor', () => { expect(appLogicSpy).toHaveBeenCalledTimes(1); expect(teardownSpy).toHaveBeenCalledTimes(3); expect(publicExecutor.simulate).toHaveBeenCalledTimes(3); - expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(3); + expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(4); expect(publicWorldStateDB.rollback).toHaveBeenCalledTimes(0); expect( arrayNonEmptyLength(processed[0].data.combinedData.publicDataUpdateRequests, PublicDataUpdateRequest.isEmpty), From bcdab8065c9dffbacbb102bf7800e0a8554b4cae Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 7 Mar 2024 11:55:12 +0000 Subject: [PATCH 07/10] Propagate unencrypted logs hash. --- .../crates/public-kernel-lib/src/common.nr | 8 +++----- yarn-project/circuits.js/src/hints/utils.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr index 664ae98d3c2d..a9393d747bef 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr @@ -51,12 +51,11 @@ pub fn initialize_emitted_end_values( circuit_outputs.end.new_note_hashes = array_to_bounded_vec(start.new_note_hashes); circuit_outputs.end.new_nullifiers = array_to_bounded_vec(start.new_nullifiers); circuit_outputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(start.new_l2_to_l1_msgs); - - // TODO - should be propagated only in initialize_end_values() since they will be squashed or cleared in the tail circuit. circuit_outputs.end.public_data_update_requests = array_to_bounded_vec(start.public_data_update_requests); + // TODO - should be propagated only in initialize_end_values() and clear them in the tail circuit. circuit_outputs.end.public_data_reads = array_to_bounded_vec(start.public_data_reads); - - // Public kernel does not modify encrypted logs values --> we just copy them to output + circuit_outputs.end.unencrypted_logs_hash = start.unencrypted_logs_hash; + circuit_outputs.end.unencrypted_log_preimages_length = start.unencrypted_log_preimages_length; circuit_outputs.end.encrypted_logs_hash = start.encrypted_logs_hash; circuit_outputs.end.encrypted_log_preimages_length = start.encrypted_log_preimages_length; @@ -65,7 +64,6 @@ pub fn initialize_emitted_end_values( let start_non_revertible = previous_kernel.public_inputs.end_non_revertible; circuit_outputs.end_non_revertible.new_note_hashes = array_to_bounded_vec(start_non_revertible.new_note_hashes); circuit_outputs.end_non_revertible.new_nullifiers = array_to_bounded_vec(start_non_revertible.new_nullifiers); - circuit_outputs.end_non_revertible.public_data_update_requests = array_to_bounded_vec(start_non_revertible.public_data_update_requests); circuit_outputs.end_non_revertible.public_data_reads = array_to_bounded_vec(start_non_revertible.public_data_reads); } diff --git a/yarn-project/circuits.js/src/hints/utils.ts b/yarn-project/circuits.js/src/hints/utils.ts index 53f6ad73b6fc..d98de228d7d3 100644 --- a/yarn-project/circuits.js/src/hints/utils.ts +++ b/yarn-project/circuits.js/src/hints/utils.ts @@ -50,9 +50,9 @@ export function concatAccumulatedData; if (numNonEmptyItems0 < arr0.length) { + const emptyItem = arr0[numNonEmptyItems0]; arr1.slice(0, numNonEmptyItems1).forEach((item, i) => { arr[i + numNonEmptyItems0] = item; arr[arr0.length + i] = emptyItem; From 6e26ba397504e6e3c615c8cd1f1d9433858eb5e5 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 7 Mar 2024 15:35:19 +0000 Subject: [PATCH 08/10] Fix merge conflicts. --- .../sequencer-client/src/simulator/public_kernel.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yarn-project/sequencer-client/src/simulator/public_kernel.ts b/yarn-project/sequencer-client/src/simulator/public_kernel.ts index a226bbbd0f30..0d6a3efa0153 100644 --- a/yarn-project/sequencer-client/src/simulator/public_kernel.ts +++ b/yarn-project/sequencer-client/src/simulator/public_kernel.ts @@ -9,6 +9,7 @@ import { elapsed } from '@aztec/foundation/timer'; import { PublicKernelAppLogicArtifact, PublicKernelSetupArtifact, + PublicKernelTailArtifact, PublicKernelTeardownArtifact, convertPublicInnerRollupInputsToWitnessMap, convertPublicInnerRollupOutputFromWitnessMap, @@ -122,12 +123,12 @@ export class RealPublicKernelCircuitSimulator implements PublicKernelCircuitSimu ): Promise { const inputWitness = convertPublicTailInputsToWitnessMap(input); const [duration, witness] = await elapsed(() => - this.wasmSimulator.simulateCircuit(inputWitness, PublicKernelTeardownArtifact), + this.wasmSimulator.simulateCircuit(inputWitness, PublicKernelTailArtifact), ); const result = convertPublicTailOutputFromWitnessMap(witness); - this.log(`Simulated public kernel teardown circuit`, { + this.log(`Simulated public kernel tail circuit`, { eventName: 'circuit-simulation', - circuitName: 'public-kernel-teardown', + circuitName: 'public-kernel-tail', duration, inputSize: input.toBuffer().length, outputSize: result.toBuffer().length, From 5405752d8c2fdf281634e8118d8cce05d311c050 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 7 Mar 2024 16:20:57 +0000 Subject: [PATCH 09/10] Fix. --- .../types/src/tests/kernel_data_builder.nr | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr index 6dfdcb705cd5..8eb8697ab472 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr @@ -176,28 +176,12 @@ impl PreviousKernelDataBuilder { pub fn append_new_nullifiers_non_revertible_from_public(&mut self, num_extra_nullifier: u64) { let index_offset = self.end_non_revertible.new_nullifiers.len(); - // Includes the tx nullifier at index 0. for i in 0..MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX { if i < num_extra_nullifier { let mock_value = self.get_mock_nullifier_value(index_offset + i); - self.end.new_nullifiers.push( - SideEffectLinkedToNoteHash { - value: silo_nullifier(self.storage_contract_address, mock_value), - note_hash: 0, - counter: self.next_sideffect_counter() - } - ); - } - } - } - - pub fn append_new_nullifiers_non_revertible(&mut self, num_extra_nullifier: u64) { - let index_offset = self.end_non_revertible.new_nullifiers.len(); - for i in 0..MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX { - if i < num_extra_nullifier { self.end_non_revertible.new_nullifiers.push( SideEffectLinkedToNoteHash { - value: self.get_mock_nullifier_value(index_offset + i), + value: silo_nullifier(self.storage_contract_address, mock_value), note_hash: 0, counter: self.next_sideffect_counter() } From 288a25a3a65d3f81ec79ae0d4acd03b8de9b1ee9 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Thu, 7 Mar 2024 19:47:59 +0000 Subject: [PATCH 10/10] Propagate reverted flag. --- .../crates/public-kernel-lib/src/public_kernel_tail.nr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 1d7be12c5ea0..d6242fe32928 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -15,6 +15,10 @@ struct PublicKernelTailCircuitPrivateInputs { } impl PublicKernelTailCircuitPrivateInputs { + fn propagate_reverted_flag(self, public_inputs: &mut PublicKernelCircuitPublicInputsBuilder) { + public_inputs.reverted = self.previous_kernel.public_inputs.reverted; + } + fn validate_inputs(self) { let previous_public_inputs = self.previous_kernel.public_inputs; assert(previous_public_inputs.needs_setup == false, "Previous kernel needs setup"); @@ -63,6 +67,8 @@ impl PublicKernelTailCircuitPrivateInputs { self.validate_inputs(); + self.propagate_reverted_flag(&mut public_inputs); + common::initialize_emitted_end_values(self.previous_kernel, &mut public_inputs); self.validate_nullifier_read_requests(&mut public_inputs);