diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e68654eef92..ab8056e8a7c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -166,5 +166,6 @@ "**/l1-contracts/lib/**": true, "**/barretenberg/cpp/build*/**": true }, - "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp" + "cmake.sourceDirectory": "${workspaceFolder}/barretenberg/cpp", + "noir.nargoPath": "./noir/noir-repo/target/release/nargo" } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr index 4231ef9f28a8..4bf24e9f18c6 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr @@ -3,11 +3,11 @@ use dep::types::{ private_kernel_data::PrivateKernelData, kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputsBuilder, PublicKernelCircuitPublicInputs}, note_hash::ScopedNoteHash, nullifier::ScopedNullifier, side_effect::Ordered, - log_hash::{NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash}, gas::Gas + log_hash::{NoteLogHash, ScopedLogHash, ScopedEncryptedLogHash}, gas::Gas, call_request::CallRequest }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX }, hash::{ compute_l2_to_l1_hash, compute_note_hash_nonce, compute_unique_note_hash, silo_note_hash, @@ -20,6 +20,10 @@ fn asc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { a.counter() < b.counter() } +fn desc_sort_by_counters(a: T, b: T) -> bool where T: Ordered { + a.counter() > b.counter() +} + // Builds: // .finish -> KernelCircuitPublicInputs (from PrivateKernelTailCircuitPrivateInputs) // .finish_to_public -> PublicKernelCircuitPublicInputs (from PrivateKernelTailToPublicCircuitPrivateInputs) @@ -92,10 +96,14 @@ impl KernelCircuitPublicInputsComposer { *self } - pub fn compose_public(&mut self) -> Self { + pub fn compose_public( + &mut self, + sorted_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], + sorted_call_requests_indexes: [u64; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX] + ) -> Self { let _ = self.compose(); - self.propagate_sorted_public_call_requests(); + self.propagate_sorted_public_call_requests(sorted_call_requests, sorted_call_requests_indexes); self.propagate_public_teardown_call_request(); *self @@ -249,9 +257,19 @@ impl KernelCircuitPublicInputsComposer { self.public_inputs.end.new_l2_to_l1_msgs = array_to_bounded_vec(accumulated_data.new_l2_to_l1_msgs); } - fn propagate_sorted_public_call_requests(&mut self) { + fn propagate_sorted_public_call_requests( + &mut self, + sorted_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], + sorted_call_requests_indexes: [u64; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX] + ) { let accumulated_data = self.previous_kernel.public_inputs.end; - self.public_inputs.end.public_call_stack = array_to_bounded_vec(accumulated_data.public_call_stack); + assert_sorted_array( + accumulated_data.public_call_stack, + sorted_call_requests, + sorted_call_requests_indexes, + desc_sort_by_counters + ); + self.public_inputs.end.public_call_stack = array_to_bounded_vec(sorted_call_requests); } fn propagate_public_teardown_call_request(&mut self) { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr index 3d08e5623267..2725c376aae3 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr @@ -3,12 +3,13 @@ use dep::types::{ abis::{ private_kernel_data::PrivateKernelData, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, note_hash::ScopedNoteHash, - nullifier::ScopedNullifier, log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash} + nullifier::ScopedNullifier, log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, + call_request::CallRequest }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX }, grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty }; @@ -24,6 +25,8 @@ struct PrivateKernelTailToPublicHints { sorted_encrypted_log_hashes_indexes: [u64; MAX_ENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX], sorted_unencrypted_log_hashes_indexes: [u64; MAX_UNENCRYPTED_LOGS_PER_TX], + sorted_call_requests: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], + sorted_call_requests_indexes: [u64; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], } struct PrivateKernelTailToPublicCircuitPrivateInputs { @@ -55,7 +58,10 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { self.hints.sorted_encrypted_log_hashes_indexes, self.hints.sorted_unencrypted_log_hashes, self.hints.sorted_unencrypted_log_hashes_indexes - ).compose_public().finish_to_public() + ).compose_public( + self.hints.sorted_call_requests, + self.hints.sorted_call_requests_indexes + ).finish_to_public() } } @@ -76,6 +82,7 @@ mod tests { }; use dep::types::{ abis::{ + call_request::CallRequest, side_effect::Ordered, kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, gas::Gas, note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, log_hash::{LogHash, ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash} @@ -178,6 +185,13 @@ mod tests { let sorted_unencrypted_log_hashes = sorted.sorted_array; let sorted_unencrypted_log_hashes_indexes = sorted.sorted_index_hints; + let sorted = sort_get_sorted_hints( + self.previous_kernel.public_call_stack.storage, + |a: CallRequest, b: CallRequest| a.counter() > b.counter() + ); + let sorted_call_requests = sorted.sorted_array; + let sorted_call_requests_indexes = sorted.sorted_index_hints; + let hints = PrivateKernelTailToPublicHints { sorted_new_note_hashes, sorted_new_note_hashes_indexes, @@ -188,7 +202,9 @@ mod tests { sorted_encrypted_log_hashes, sorted_encrypted_log_hashes_indexes, sorted_unencrypted_log_hashes, - sorted_unencrypted_log_hashes_indexes + sorted_unencrypted_log_hashes_indexes, + sorted_call_requests, + sorted_call_requests_indexes }; let kernel = PrivateKernelTailToPublicCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), hints }; diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index ec01c41e546b..1c4989c97b03 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -3,10 +3,12 @@ import { CallRequest, GasSettings, LogHash, + MAX_NEW_NULLIFIERS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, Nullifier, PartialPrivateTailPublicInputsForPublic, PrivateKernelTailCircuitPublicInputs, + PublicAccumulatedDataBuilder, PublicCallRequest, computeContractClassId, getContractClassFromArtifact, @@ -69,20 +71,34 @@ export const mockTx = ( data.forRollup = undefined; data.forPublic = PartialPrivateTailPublicInputsForPublic.empty(); - data.forPublic.endNonRevertibleData.newNullifiers[0] = firstNullifier; - publicCallRequests = publicCallRequests.length ? publicCallRequests.slice().sort((a, b) => b.callContext.sideEffectCounter - a.callContext.sideEffectCounter) : times(totalPublicCallRequests, i => makePublicCallRequest(seed + 0x100 + i)); - data.forPublic.end.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i => - i < numberOfRevertiblePublicCallRequests ? publicCallRequests[i].toCallRequest() : CallRequest.empty(), - ); - data.forPublic.endNonRevertibleData.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i => - i < numberOfNonRevertiblePublicCallRequests - ? publicCallRequests[numberOfRevertiblePublicCallRequests + i].toCallRequest() - : CallRequest.empty(), - ); + const revertibleBuilder = new PublicAccumulatedDataBuilder(); + const nonRevertibleBuilder = new PublicAccumulatedDataBuilder(); + + const nonRevertibleNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, Nullifier.empty); + nonRevertibleNullifiers[0] = firstNullifier; + + data.forPublic.endNonRevertibleData = nonRevertibleBuilder + .withNewNullifiers(nonRevertibleNullifiers) + .withPublicCallStack( + makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i => + i < numberOfNonRevertiblePublicCallRequests + ? publicCallRequests[numberOfRevertiblePublicCallRequests + i].toCallRequest() + : CallRequest.empty(), + ), + ) + .build(); + + data.forPublic.end = revertibleBuilder + .withPublicCallStack( + makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, i => + i < numberOfRevertiblePublicCallRequests ? publicCallRequests[i].toCallRequest() : CallRequest.empty(), + ), + ) + .build(); data.forPublic.publicTeardownCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, () => CallRequest.empty()); data.forPublic.publicTeardownCallStack[0] = publicTeardownCallRequest.isEmpty() diff --git a/yarn-project/circuits.js/src/structs/call_request.ts b/yarn-project/circuits.js/src/structs/call_request.ts index 1b5ca4de9f53..f3c96c1d42b2 100644 --- a/yarn-project/circuits.js/src/structs/call_request.ts +++ b/yarn-project/circuits.js/src/structs/call_request.ts @@ -41,6 +41,10 @@ export class CallRequest { ); } + get counter() { + return this.startSideEffectCounter.toNumber(); + } + /** * Deserializes from a buffer or reader. * @param buffer - Buffer or reader to read from. diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 15e1f23da4c4..96ca0032582d 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -9,31 +9,34 @@ export * from './context/public_context_inputs.js'; export * from './contract_storage_read.js'; export * from './contract_storage_update_request.js'; export * from './function_data.js'; +export * from './gas.js'; export * from './gas_fees.js'; export * from './gas_settings.js'; -export * from './gas.js'; export * from './global_variables.js'; export * from './header.js'; export * from './kernel/combined_accumulated_data.js'; -export * from './kernel/private_accumulated_data.js'; -export * from './kernel/public_accumulated_data.js'; export * from './kernel/combined_constant_data.js'; +export * from './kernel/kernel_circuit_public_inputs.js'; +export * from './kernel/kernel_data.js'; +export * from './kernel/private_accumulated_data.js'; export * from './kernel/private_call_data.js'; -export * from './kernel/private_kernel_init_circuit_private_inputs.js'; export * from './kernel/private_kernel_circuit_public_inputs.js'; export * from './kernel/private_kernel_data.js'; +export * from './kernel/private_kernel_init_circuit_private_inputs.js'; export * from './kernel/private_kernel_inner_circuit_private_inputs.js'; export * from './kernel/private_kernel_reset_circuit_private_inputs.js'; export * from './kernel/private_kernel_reset_circuit_private_inputs_variants.js'; export * from './kernel/private_kernel_tail_circuit_private_inputs.js'; export * from './kernel/private_kernel_tail_circuit_public_inputs.js'; +export * from './kernel/public_accumulated_data.js'; +export * from './kernel/public_accumulated_data_builder.js'; 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/kernel_circuit_public_inputs.js'; -export * from './kernel/kernel_data.js'; +export * from './key_validation_request.js'; +export * from './key_validation_request_and_generator.js'; export * from './l2_to_l1_message.js'; export * from './log_hash.js'; export * from './max_block_number.js'; @@ -41,9 +44,6 @@ export * from './membership_witness.js'; export * from './non_existent_read_request_hints.js'; export * from './note_hash.js'; export * from './nullifier.js'; -export * from './key_validation_request.js'; -export * from './key_validation_request_and_generator.js'; -export * from './scoped_key_validation_request_and_generator.js'; export * from './parity/base_parity_inputs.js'; export * from './parity/parity_public_inputs.js'; export * from './parity/root_parity_input.js'; @@ -72,6 +72,7 @@ export * from './rollup/previous_rollup_data.js'; export * from './rollup/root_rollup.js'; export * from './rollup/state_diff_hints.js'; export * from './rollup_validation_requests.js'; +export * from './scoped_key_validation_request_and_generator.js'; export * from './shared.js'; export * from './side_effects.js'; export * from './state_reference.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts index fb5c6e295aaa..151dbdc1e1cf 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_private_inputs.ts @@ -5,9 +5,11 @@ import { MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, } from '../../constants.gen.js'; import { countAccumulatedItems } from '../../utils/index.js'; +import { CallRequest } from '../call_request.js'; import { NoteLogHash, ScopedEncryptedLogHash, ScopedLogHash } from '../log_hash.js'; import { ScopedNoteHash } from '../note_hash.js'; import { ScopedNullifier } from '../nullifier.js'; @@ -55,6 +57,14 @@ export class PrivateKernelTailHints { * The sorted encrypted log hashes indexes. Maps original to sorted. */ public sortedUnencryptedLogHashesIndexes: Tuple, + /** + * The sorted public call requests. + */ + public sortedCallRequests: Tuple, + /** + * The sorted public call requests indexes. Maps original to sorted. + */ + public sortedCallRequestsIndexes: Tuple, ) {} toBuffer() { @@ -69,6 +79,8 @@ export class PrivateKernelTailHints { this.sortedEncryptedLogHashesIndexes, this.sortedUnencryptedLogHashes, this.sortedUnencryptedLogHashesIndexes, + this.sortedCallRequests, + this.sortedCallRequestsIndexes, ); } @@ -90,6 +102,8 @@ export class PrivateKernelTailHints { reader.readNumbers(MAX_ENCRYPTED_LOGS_PER_TX), reader.readArray(MAX_UNENCRYPTED_LOGS_PER_TX, ScopedLogHash), reader.readNumbers(MAX_UNENCRYPTED_LOGS_PER_TX), + reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), + reader.readNumbers(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX), ); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts index c5285d3206e3..0bdf2481f701 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data.ts @@ -58,7 +58,7 @@ export class PublicAccumulatedData { /** * Current public call stack. */ - public publicCallStack: Tuple, + public readonly publicCallStack: Tuple, /** Gas used so far by the transaction. */ public gasUsed: Gas, diff --git a/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data_builder.ts b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data_builder.ts new file mode 100644 index 000000000000..cde9807a0535 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/kernel/public_accumulated_data_builder.ts @@ -0,0 +1,154 @@ +import { padArrayEnd } from '@aztec/foundation/collection'; +import { Fr } from '@aztec/foundation/fields'; + +import { + MAX_ENCRYPTED_LOGS_PER_TX, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, + MAX_NEW_NOTE_HASHES_PER_TX, + MAX_NEW_NULLIFIERS_PER_TX, + MAX_NOTE_ENCRYPTED_LOGS_PER_TX, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX, +} from '../../constants.gen.js'; +import { CallRequest } from '../call_request.js'; +import { Gas } from '../gas.js'; +import { LogHash } from '../log_hash.js'; +import { NoteHash } from '../note_hash.js'; +import { Nullifier } from '../nullifier.js'; +import { PublicDataUpdateRequest } from '../public_data_update_request.js'; +import { PublicAccumulatedData } from './public_accumulated_data.js'; + +/** + * TESTS-ONLY CLASS + * Builder for PublicAccumulatedData, used to conveniently construct instances for testing, + * as PublicAccumulatedData is (or will shortly be) immutable. + * + */ +export class PublicAccumulatedDataBuilder { + private newNoteHashes: NoteHash[] = []; + private newNullifiers: Nullifier[] = []; + private newL2ToL1Msgs: Fr[] = []; + private noteEncryptedLogsHashes: LogHash[] = []; + private encryptedLogsHashes: LogHash[] = []; + private unencryptedLogsHashes: LogHash[] = []; + private publicDataUpdateRequests: PublicDataUpdateRequest[] = []; + private publicCallStack: CallRequest[] = []; + private gasUsed: Gas = Gas.empty(); + + pushNewNoteHash(newNoteHash: NoteHash) { + this.newNoteHashes.push(newNoteHash); + return this; + } + + withNewNoteHashes(newNoteHashes: NoteHash[]) { + this.newNoteHashes = newNoteHashes; + return this; + } + + pushNewNullifier(newNullifier: Nullifier) { + this.newNullifiers.push(newNullifier); + return this; + } + + withNewNullifiers(newNullifiers: Nullifier[]) { + this.newNullifiers = newNullifiers; + return this; + } + + pushNewL2ToL1Msg(newL2ToL1Msg: Fr) { + this.newL2ToL1Msgs.push(newL2ToL1Msg); + return this; + } + + withNewL2ToL1Msgs(newL2ToL1Msgs: Fr[]) { + this.newL2ToL1Msgs = newL2ToL1Msgs; + return this; + } + + pushNoteEncryptedLogsHash(noteEncryptedLogsHash: LogHash) { + this.noteEncryptedLogsHashes.push(noteEncryptedLogsHash); + return this; + } + + withNoteEncryptedLogsHashes(noteEncryptedLogsHashes: LogHash[]) { + this.noteEncryptedLogsHashes = noteEncryptedLogsHashes; + return this; + } + + pushEncryptedLogsHash(encryptedLogsHash: LogHash) { + this.encryptedLogsHashes.push(encryptedLogsHash); + return this; + } + + withEncryptedLogsHashes(encryptedLogsHashes: LogHash[]) { + this.encryptedLogsHashes = encryptedLogsHashes; + return this; + } + + pushUnencryptedLogsHash(unencryptedLogsHash: LogHash) { + this.unencryptedLogsHashes.push(unencryptedLogsHash); + return this; + } + + withUnencryptedLogsHashes(unencryptedLogsHashes: LogHash[]) { + this.unencryptedLogsHashes = unencryptedLogsHashes; + return this; + } + + pushPublicDataUpdateRequest(publicDataUpdateRequest: PublicDataUpdateRequest) { + this.publicDataUpdateRequests.push(publicDataUpdateRequest); + return this; + } + + withPublicDataUpdateRequests(publicDataUpdateRequests: PublicDataUpdateRequest[]) { + this.publicDataUpdateRequests = publicDataUpdateRequests; + return this; + } + + pushPublicCall(publicCall: CallRequest) { + this.publicCallStack.push(publicCall); + return this; + } + + withPublicCallStack(publicCallStack: CallRequest[]) { + this.publicCallStack = publicCallStack; + return this; + } + + withGasUsed(gasUsed: Gas) { + this.gasUsed = gasUsed; + return this; + } + + build(): PublicAccumulatedData { + return new PublicAccumulatedData( + padArrayEnd(this.newNoteHashes, NoteHash.empty(), MAX_NEW_NOTE_HASHES_PER_TX), + padArrayEnd(this.newNullifiers, Nullifier.empty(), MAX_NEW_NULLIFIERS_PER_TX), + padArrayEnd(this.newL2ToL1Msgs, Fr.ZERO, MAX_NEW_L2_TO_L1_MSGS_PER_CALL), + padArrayEnd(this.noteEncryptedLogsHashes, LogHash.empty(), MAX_NOTE_ENCRYPTED_LOGS_PER_TX), + padArrayEnd(this.encryptedLogsHashes, LogHash.empty(), MAX_ENCRYPTED_LOGS_PER_TX), + padArrayEnd(this.unencryptedLogsHashes, LogHash.empty(), MAX_UNENCRYPTED_LOGS_PER_TX), + padArrayEnd( + this.publicDataUpdateRequests, + PublicDataUpdateRequest.empty(), + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ), + padArrayEnd(this.publicCallStack, CallRequest.empty(), MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX), + this.gasUsed, + ); + } + + static fromPublicAccumulatedData(publicAccumulatedData: PublicAccumulatedData): PublicAccumulatedDataBuilder { + return new PublicAccumulatedDataBuilder() + .withNewNoteHashes(publicAccumulatedData.newNoteHashes) + .withNewNullifiers(publicAccumulatedData.newNullifiers) + .withNewL2ToL1Msgs(publicAccumulatedData.newL2ToL1Msgs) + .withNoteEncryptedLogsHashes(publicAccumulatedData.noteEncryptedLogsHashes) + .withEncryptedLogsHashes(publicAccumulatedData.encryptedLogsHashes) + .withUnencryptedLogsHashes(publicAccumulatedData.unencryptedLogsHashes) + .withPublicDataUpdateRequests(publicAccumulatedData.publicDataUpdateRequests) + .withPublicCallStack(publicAccumulatedData.publicCallStack) + .withGasUsed(publicAccumulatedData.gasUsed); + } +} diff --git a/yarn-project/circuits.js/src/utils/index.ts b/yarn-project/circuits.js/src/utils/index.ts index 879d3a0f5a27..62d4b0321789 100644 --- a/yarn-project/circuits.js/src/utils/index.ts +++ b/yarn-project/circuits.js/src/utils/index.ts @@ -36,7 +36,10 @@ export function mergeAccumulatedData( } // Sort items by their counters in ascending order. All empty items (counter === 0) are padded to the right. -export function sortByCounter(arr: Tuple): Tuple { +export function sortByCounter( + arr: Tuple, + ascending: boolean = true, +): Tuple { return [...arr].sort((a, b) => { if (a.counter === b.counter) { return 0; @@ -47,13 +50,14 @@ export function sortByCounter(arr if (b.isEmpty()) { return -1; // Move non-empty items to the left. } - return a.counter - b.counter; + return ascending ? a.counter - b.counter : b.counter - a.counter; }) as Tuple; } export function sortByCounterGetSortedHints( arr: Tuple, length: N = arr.length as N, // Need this for ts to infer the return Tuple length. + ascending: boolean = true, ): [Tuple, Tuple] { const itemsWithIndexes = arr.map((item, i) => ({ item, @@ -61,7 +65,7 @@ export function sortByCounterGetSortedHints item.isEmpty(), })); - const sorted = sortByCounter(itemsWithIndexes); + const sorted = sortByCounter(itemsWithIndexes, ascending); const items = sorted.map(({ item }) => item) as Tuple; const indexHints = makeTuple(length, () => 0); 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 21dd950fce31..ce4dfdcd75ba 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -201,6 +201,7 @@ import type { PrivateKernelTailCircuitPrivateInputs as PrivateKernelTailCircuitPrivateInputsNoir, PrivateKernelTailHints as PrivateKernelTailHintsNoir, PrivateKernelTailToPublicCircuitPrivateInputs as PrivateKernelTailToPublicCircuitPrivateInputsNoir, + PrivateKernelTailToPublicHints as PrivateKernelTailToPublicHintsNoir, PublicAccumulatedData as PublicAccumulatedDataNoir, PublicCallData as PublicCallDataNoir, PublicCallStackItem as PublicCallStackItemNoir, @@ -1646,6 +1647,23 @@ function mapPrivateKernelTailHintsToNoir(inputs: PrivateKernelTailHints): Privat }; } +function mapPrivateKernelTailToPublicHintsToNoir(inputs: PrivateKernelTailHints): PrivateKernelTailToPublicHintsNoir { + return { + sorted_new_note_hashes: mapTuple(inputs.sortedNewNoteHashes, mapScopedNoteHashToNoir), + sorted_new_note_hashes_indexes: mapTuple(inputs.sortedNewNoteHashesIndexes, mapNumberToNoir), + sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapScopedNullifierToNoir), + sorted_new_nullifiers_indexes: mapTuple(inputs.sortedNewNullifiersIndexes, mapNumberToNoir), + sorted_note_encrypted_log_hashes: mapTuple(inputs.sortedNoteEncryptedLogHashes, mapNoteLogHashToNoir), + sorted_note_encrypted_log_hashes_indexes: mapTuple(inputs.sortedNoteEncryptedLogHashesIndexes, mapNumberToNoir), + sorted_encrypted_log_hashes: mapTuple(inputs.sortedEncryptedLogHashes, mapScopedEncryptedLogHashToNoir), + sorted_encrypted_log_hashes_indexes: mapTuple(inputs.sortedEncryptedLogHashesIndexes, mapNumberToNoir), + sorted_unencrypted_log_hashes: mapTuple(inputs.sortedUnencryptedLogHashes, mapScopedLogHashToNoir), + sorted_unencrypted_log_hashes_indexes: mapTuple(inputs.sortedUnencryptedLogHashesIndexes, mapNumberToNoir), + sorted_call_requests: mapTuple(inputs.sortedCallRequests, mapCallRequestToNoir), + sorted_call_requests_indexes: mapTuple(inputs.sortedCallRequestsIndexes, mapNumberToNoir), + }; +} + function mapPrivateKernelResetHintsToNoir< NH_RR_PENDING extends number, NH_RR_SETTLED extends number, @@ -1721,7 +1739,7 @@ export function mapPrivateKernelTailToPublicCircuitPrivateInputsToNoir( ): PrivateKernelTailToPublicCircuitPrivateInputsNoir { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), - hints: mapPrivateKernelTailHintsToNoir(inputs.hints), + hints: mapPrivateKernelTailToPublicHintsToNoir(inputs.hints), }; } diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts index 4a0781fdb8a3..e4853eca8d43 100644 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts @@ -3,6 +3,7 @@ import { MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, type PrivateKernelCircuitPublicInputs, PrivateKernelTailHints, @@ -35,6 +36,12 @@ export function buildPrivateKernelTailHints(publicInputs: PrivateKernelCircuitPu MAX_UNENCRYPTED_LOGS_PER_TX, ); + const [sortedCallRequests, sortedCallRequestsIndexes] = sortByCounterGetSortedHints( + publicInputs.end.publicCallStack, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, + /* ascending */ false, + ); + return new PrivateKernelTailHints( sortedNoteHashes, sortedNoteHashesIndexes, @@ -46,5 +53,7 @@ export function buildPrivateKernelTailHints(publicInputs: PrivateKernelCircuitPu sortedEncryptedLogHashesIndexes, sortedUnencryptedLogHashes, sortedUnencryptedLogHashesIndexes, + sortedCallRequests, + sortedCallRequestsIndexes, ); } diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 3abecbcc7ca6..efdc3d85e2d2 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -24,18 +24,13 @@ import { } from '@aztec/circuit-types'; import { AztecAddress, - CallRequest, type CompleteAddress, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, type PartialAddress, - type PrivateKernelTailCircuitPublicInputs, - type PublicCallRequest, computeContractClassId, getContractClassFromArtifact, } from '@aztec/circuits.js'; import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash'; import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; -import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection'; import { type Fq, Fr } from '@aztec/foundation/fields'; import { SerialQueue } from '@aztec/foundation/fifo'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; @@ -655,10 +650,6 @@ export class PXEService implements PXE { const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult); const teardownPublicFunction = collectPublicTeardownFunctionCall(executionResult); - // HACK(#1639): Manually patches the ordering of the public call stack - // TODO(#757): Enforce proper ordering of enqueued public calls - await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions); - const tx = new Tx( publicInputs, proof.binaryProof, @@ -709,76 +700,6 @@ export class PXEService implements PXE { ); } - // HACK(#1639): this is a hack to fix ordering of public calls enqueued in the call stack. Since the private kernel - // cannot keep track of side effects that happen after or before a nested call, we override the public call stack - // it emits with whatever we got from the simulator collected enqueued calls. As a sanity check, we at least verify - // that the elements are the same, so we are only tweaking their ordering. - // See yarn-project/end-to-end/src/e2e_ordering.test.ts - // See https://github.com/AztecProtocol/aztec-packages/issues/1615 - // TODO(#757): Enforce proper ordering of enqueued public calls - private async patchPublicCallStackOrdering( - publicInputs: PrivateKernelTailCircuitPublicInputs, - enqueuedPublicCalls: PublicCallRequest[], - ) { - if (!publicInputs.forPublic) { - return; - } - - const enqueuedPublicCallStackItems = await Promise.all(enqueuedPublicCalls.map(c => c.toCallRequest())); - - // Validate all items in enqueued public calls are in the kernel emitted stack - const enqueuedRevertiblePublicCallStackItems = enqueuedPublicCallStackItems.filter(enqueued => - publicInputs.forPublic!.end.publicCallStack.find(item => item.equals(enqueued)), - ); - - const revertibleStackSize = arrayNonEmptyLength(publicInputs.forPublic.end.publicCallStack, item => item.isEmpty()); - - if (enqueuedRevertiblePublicCallStackItems.length !== revertibleStackSize) { - throw new Error( - `Enqueued revertible public function calls and revertible public call stack do not match.\nEnqueued calls: ${enqueuedRevertiblePublicCallStackItems - .map(h => h.hash.toString()) - .join(', ')}\nPublic call stack: ${publicInputs.forPublic.end.publicCallStack - .map(i => i.toString()) - .join(', ')}`, - ); - } - - // Override kernel output - publicInputs.forPublic.end.publicCallStack = padArrayEnd( - enqueuedRevertiblePublicCallStackItems, - CallRequest.empty(), - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - ); - - // Do the same for non-revertible - - const enqueuedNonRevertiblePublicCallStackItems = enqueuedPublicCallStackItems.filter(enqueued => - publicInputs.forPublic!.endNonRevertibleData.publicCallStack.find(item => item.equals(enqueued)), - ); - - const nonRevertibleStackSize = arrayNonEmptyLength( - publicInputs.forPublic.endNonRevertibleData.publicCallStack, - item => item.isEmpty(), - ); - - if (enqueuedNonRevertiblePublicCallStackItems.length !== nonRevertibleStackSize) { - throw new Error( - `Enqueued non-revertible public function calls and non-revertible public call stack do not match.\nEnqueued calls: ${enqueuedNonRevertiblePublicCallStackItems - .map(h => h.hash.toString()) - .join(', ')}\nPublic call stack: ${publicInputs.forPublic.endNonRevertibleData.publicCallStack - .map(i => i.toString()) - .join(', ')}`, - ); - } - - // Override kernel output - publicInputs.forPublic.endNonRevertibleData.publicCallStack = padArrayEnd( - enqueuedNonRevertiblePublicCallStackItems, - CallRequest.empty(), - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - ); - } - public async isGlobalStateSynchronized() { return await this.synchronizer.isGlobalStateSynchronized(); } diff --git a/yarn-project/simulator/package.json b/yarn-project/simulator/package.json index dd6040e698b6..f99e74aa4b27 100644 --- a/yarn-project/simulator/package.json +++ b/yarn-project/simulator/package.json @@ -2,7 +2,10 @@ "name": "@aztec/simulator", "version": "0.1.0", "type": "module", - "exports": { ".": "./dest/index.js", "./avm/fixtures": "./dest/avm/fixtures/index.js" }, + "exports": { + ".": "./dest/index.js", + "./avm/fixtures": "./dest/avm/fixtures/index.js" + }, "typedocOptions": { "entryPoints": [ "./src/index.ts" diff --git a/yarn-project/simulator/src/mocks/fixtures.ts b/yarn-project/simulator/src/mocks/fixtures.ts index 097133183a60..a56bc4cdca86 100644 --- a/yarn-project/simulator/src/mocks/fixtures.ts +++ b/yarn-project/simulator/src/mocks/fixtures.ts @@ -3,12 +3,9 @@ import { ARGS_LENGTH, type AztecAddress, CallContext, - CallRequest, type ContractStorageUpdateRequest, Fr, Gas, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - type PrivateKernelTailCircuitPublicInputs, type PublicCallRequest, } from '@aztec/circuits.js'; import { makeAztecAddress, makeSelector } from '@aztec/circuits.js/testing'; @@ -143,28 +140,3 @@ export const makeFunctionCall = ( isStatic = false, returnTypes = [], ) => ({ name, to, selector, type, args, isStatic, returnTypes }); - -export function addKernelPublicCallStack( - kernelOutput: PrivateKernelTailCircuitPublicInputs, - calls: { - setupCalls: PublicCallRequest[]; - appLogicCalls: PublicCallRequest[]; - teardownCall: PublicCallRequest; - }, -) { - // the first two calls are non-revertible - // the first is for setup, the second is for teardown - kernelOutput.forPublic!.endNonRevertibleData.publicCallStack = padArrayEnd( - // this is a stack, so the first item is the last call - // and callRequests is in the order of the calls - [calls.teardownCall.toCallRequest(), ...calls.setupCalls.map(c => c.toCallRequest())], - CallRequest.empty(), - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - ); - - kernelOutput.forPublic!.end.publicCallStack = padArrayEnd( - calls.appLogicCalls.map(c => c.toCallRequest()), - CallRequest.empty(), - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - ); -} diff --git a/yarn-project/simulator/src/public/setup_phase_manager.test.ts b/yarn-project/simulator/src/public/setup_phase_manager.test.ts index c8be4e8a64c1..136d91646bb6 100644 --- a/yarn-project/simulator/src/public/setup_phase_manager.test.ts +++ b/yarn-project/simulator/src/public/setup_phase_manager.test.ts @@ -1,6 +1,5 @@ import { mockTx } from '@aztec/circuit-types'; -import { CallRequest, GlobalVariables, Header, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX } from '@aztec/circuits.js'; -import { makeTuple } from '@aztec/foundation/array'; +import { GlobalVariables, Header, PublicAccumulatedDataBuilder } from '@aztec/circuits.js'; import { type PublicExecutor } from '@aztec/simulator'; import { type MerkleTreeOperations, type TreeInfo } from '@aztec/world-state'; @@ -50,11 +49,17 @@ describe('setup_phase_manager', () => { it('does not extract non-revertible calls when none exist', function () { const tx = mockTx(); - tx.data.forPublic!.end.publicCallStack = makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty); - tx.data.forPublic!.endNonRevertibleData.publicCallStack = makeTuple( - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - CallRequest.empty, - ); + + tx.data.forPublic!.end = PublicAccumulatedDataBuilder.fromPublicAccumulatedData(tx.data.forPublic!.end) + .withPublicCallStack([]) + .build(); + + tx.data.forPublic!.endNonRevertibleData = PublicAccumulatedDataBuilder.fromPublicAccumulatedData( + tx.data.forPublic!.endNonRevertibleData, + ) + .withPublicCallStack([]) + .build(); + const enqueuedNonRevertibleCalls = phaseManager.extractEnqueuedPublicCalls(tx); expect(enqueuedNonRevertibleCalls).toEqual([]);