From 04d5a515b1ebb7ae25d681538b1925411f426f7c Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 14 Nov 2024 09:43:43 +0000 Subject: [PATCH 01/25] separate base rollups --- .../crates/rollup-lib/src/base/mod.nr | 1 - .../src/base/private_base_rollup.nr | 342 +++++++++++++- .../rollup-lib/src/base/public_base_rollup.nr | 441 +++++++++++++++++- .../types/src/merkle_tree/indexed_tree.nr | 58 +++ 4 files changed, 796 insertions(+), 46 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr index 6bee28884ee..d883ab9dff2 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr @@ -1,4 +1,3 @@ -mod base_rollup_inputs; mod components; mod private_base_rollup; mod public_base_rollup; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 848147c61b9..85a74086ea6 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -1,22 +1,41 @@ use crate::{ abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, + base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, constant_rollup_data::ConstantRollupData, }, - base::{ - base_rollup_inputs::{BaseRollupInputs, KernelData}, - components::private_tube_data::PrivateTubeData, - state_diff_hints::StateDiffHints, - }, + base::{components::private_tube_data::PrivateTubeData, state_diff_hints::StateDiffHints}, + components::{compute_kernel_out_hash, compute_tx_effects_hash}, }; use dep::types::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + kernel_circuit_public_inputs::KernelCircuitPublicInputs, + nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, + }, + address::AztecAddress, constants::{ - ARCHIVE_HEIGHT, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PRIVATE_KERNEL_EMPTY_INDEX, - PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, + ARCHIVE_HEIGHT, FEE_JUICE_ADDRESS, MAX_NOTE_HASHES_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + NOTE_HASH_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, + PRIVATE_KERNEL_EMPTY_INDEX, PUBLIC_DATA_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, }, - data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, - merkle_tree::MembershipWitness, + data::{ + hash::{compute_public_data_tree_index, compute_public_data_tree_value}, + public_data_hint::PublicDataHint, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, + }, + hash::silo_l2_to_l1_message, + merkle_tree::{ + append_only_tree, assert_check_membership, calculate_empty_tree_root, + calculate_subtree_root, indexed_tree, MembershipWitness, + }, + messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, + storage::map::derive_storage_slot_in_map, + traits::is_empty, + utils::{arrays::find_index_hint, field::{full_field_greater_than, full_field_less_than}}, }; global ALLOWED_PREVIOUS_CIRCUITS = [PRIVATE_KERNEL_EMPTY_INDEX, TUBE_VK_INDEX]; @@ -29,10 +48,9 @@ pub struct PrivateBaseRollupInputs { state_diff_hints: StateDiffHints, fee_payer_fee_juice_balance_read_hint: PublicDataHint, - sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + fee_write_low_leaf_preimage: PublicDataTreeLeafPreimage, + fee_write_low_leaf_membership_witness: MembershipWitness, + fee_write_sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT], archive_root_membership_witness: MembershipWitness, constants: ConstantRollupData, @@ -54,19 +72,293 @@ impl PrivateBaseRollupInputs { let transaction_fee = self.compute_transaction_fee(); - BaseRollupInputs { - kernel_data: KernelData { public_inputs: self.tube_data.public_inputs }, - start: self.start, - state_diff_hints: self.state_diff_hints, + // Verify the kernel chain_id and versions + assert( + self.tube_data.public_inputs.constants.tx_context.chain_id + == self.constants.global_variables.chain_id, + "kernel chain_id does not match the rollup chain_id", + ); + assert( + self.tube_data.public_inputs.constants.tx_context.version + == self.constants.global_variables.version, + "kernel version does not match the rollup version", + ); + assert( + self.tube_data.public_inputs.constants.vk_tree_root == self.constants.vk_tree_root, + "kernel vk_tree_root does not match the rollup vk_tree_root", + ); + assert( + self.tube_data.public_inputs.constants.protocol_contract_tree_root + == self.constants.protocol_contract_tree_root, + "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", + ); + + // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. + // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? + assert( + self.tube_data.public_inputs.constants.global_variables.is_empty() + | ( + self.tube_data.public_inputs.constants.global_variables + == self.constants.global_variables + ), + "kernel global variables do not match the rollup global variables", + ); + + self.validate_kernel_start_state(); + + let rollup_validation_requests = self.tube_data.public_inputs.rollup_validation_requests; + + // Verify the max block number + // TODO #5345: why is block_number a Field and not u32? + if rollup_validation_requests.max_block_number.is_some() { + assert( + self.constants.global_variables.block_number as u32 + <= rollup_validation_requests.max_block_number.unwrap_unchecked(), + "kernel max_block_number is smaller than block number", + ); + } + + let commitments_tree_subroot = self.calculate_commitments_subtree(); + + let empty_commitments_subtree_root = calculate_empty_tree_root(NOTE_HASH_SUBTREE_HEIGHT); + + let end_note_hash_tree_snapshot = append_only_tree::insert_subtree_to_snapshot_tree( + self.start.note_hash_tree, + self.state_diff_hints.note_hash_subtree_sibling_path, + empty_commitments_subtree_root, + commitments_tree_subroot, + NOTE_HASH_SUBTREE_HEIGHT as u8, + ); + + // Insert nullifiers: + let end_nullifier_tree_snapshot = + self.check_nullifier_tree_non_membership_and_insert_to_tree(); + + // Write fee to public data tree + let fee_public_data_write = self.build_fee_public_data_write(transaction_fee); + let end_public_data_tree_snapshot = + self.insert_fee_public_data_write(fee_public_data_write); + let mut all_public_data_update_requests = + [PublicDataWrite::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; + all_public_data_update_requests[0] = fee_public_data_write; + + // Calculate the tx effects hash of the transaction + let siloed_l2_to_l1_msgs = self.tube_data.public_inputs.end.l2_to_l1_msgs.map( + |message: ScopedL2ToL1Message| silo_l2_to_l1_message( + message, + self.tube_data.public_inputs.constants.tx_context.version, + self.tube_data.public_inputs.constants.tx_context.chain_id, + ), + ); + let out_hash = compute_kernel_out_hash(siloed_l2_to_l1_msgs); + let tx_effects_hash = compute_tx_effects_hash( + self.tube_data.public_inputs.end, + self.tube_data.public_inputs.revert_code, transaction_fee, - fee_payer_fee_juice_balance_read_hint: self.fee_payer_fee_juice_balance_read_hint, - sorted_public_data_writes: self.sorted_public_data_writes, - sorted_public_data_writes_indexes: self.sorted_public_data_writes_indexes, - low_public_data_writes_preimages: self.low_public_data_writes_preimages, - low_public_data_writes_witnesses: self.low_public_data_writes_witnesses, - archive_root_membership_witness: self.archive_root_membership_witness, + all_public_data_update_requests, + out_hash, + ); + + // Perform membership checks that the notes provided exist within the historical trees data + self.perform_archive_membership_checks(); + + BaseOrMergeRollupPublicInputs { + rollup_type: BASE_ROLLUP_TYPE, + num_txs: 1, constants: self.constants, + start: self.start, + end: PartialStateReference { + note_hash_tree: end_note_hash_tree_snapshot, + nullifier_tree: end_nullifier_tree_snapshot, + public_data_tree: end_public_data_tree_snapshot, + }, + txs_effects_hash: tx_effects_hash, + out_hash, + accumulated_fees: transaction_fee, } - .base_rollup_circuit() } + + // TODO(Kev): This should say calculate_commitments_subtree_root + // Cpp code says calculate_commitments_subtree, so I'm leaving it as is for now + fn calculate_commitments_subtree(self) -> Field { + calculate_subtree_root(self.tube_data.public_inputs.end.note_hashes) + } + + fn check_nullifier_tree_non_membership_and_insert_to_tree(self) -> AppendOnlyTreeSnapshot { + indexed_tree::batch_insert( + self.start.nullifier_tree, + self.tube_data.public_inputs.end.nullifiers, + self.state_diff_hints.sorted_nullifiers, + self.state_diff_hints.sorted_nullifier_indexes, + self.state_diff_hints.nullifier_subtree_sibling_path, + self.state_diff_hints.nullifier_predecessor_preimages, + self.state_diff_hints.nullifier_predecessor_membership_witnesses.map( + |witness: MembershipWitness| { + MembershipWitness { + leaf_index: witness.leaf_index, + sibling_path: witness.sibling_path, + } + }, + ), + |low_leaf: NullifierLeafPreimage, nullifier: Field| { + // Is valid low leaf + let is_less_than_nullifier = full_field_less_than(low_leaf.nullifier, nullifier); + let is_next_greater_than = full_field_less_than(nullifier, low_leaf.next_nullifier); + + (!low_leaf.is_empty()) + & is_less_than_nullifier + & ( + is_next_greater_than + | ((low_leaf.next_index == 0) & (low_leaf.next_nullifier == 0)) + ) + }, + |low_leaf: NullifierLeafPreimage, nullifier: Field, nullifier_index: u32| { + // Update low leaf + NullifierLeafPreimage { + nullifier: low_leaf.nullifier, + next_nullifier: nullifier, + next_index: nullifier_index, + } + }, + |nullifier: Field, low_leaf: NullifierLeafPreimage| { + // Build insertion leaf + NullifierLeafPreimage { + nullifier: nullifier, + next_nullifier: low_leaf.next_nullifier, + next_index: low_leaf.next_index, + } + }, + [0; NULLIFIER_SUBTREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT], + ) + } + + fn create_nullifier_subtree(leaves: [NullifierLeafPreimage; N]) -> Field { + calculate_subtree_root(leaves.map(|leaf: NullifierLeafPreimage| leaf.hash())) + } + + fn validate_kernel_start_state(self) { + let kernel_state = self.tube_data.public_inputs.start_state; + if !is_empty(kernel_state) { + assert( + kernel_state.note_hash_tree.eq(self.start.note_hash_tree), + "Mismatch start state for note hash tree", + ); + assert( + kernel_state.nullifier_tree.eq(self.start.nullifier_tree), + "Mismatch start state for nullifier tree", + ); + assert( + kernel_state.public_data_tree.eq(self.start.public_data_tree), + "Mismatch start state for public data tree", + ); + } + } + + fn build_fee_public_data_write(self, tx_fee: Field) -> PublicDataWrite { + let fee_payer = self.tube_data.public_inputs.fee_payer; + + let read_hint = self.fee_payer_fee_juice_balance_read_hint; + let leaf_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + + // Otherwise, build a new one to be inserted into the protocol update requests at the end of the array. + read_hint.validate(self.start.public_data_tree.root); + + let balance = read_hint.value; + assert(read_hint.leaf_slot == leaf_slot, "Wrong leaf slot for Fee Juice balance read hint"); + assert(!balance.lt(tx_fee), "Not enough balance for fee payer to pay for transaction"); + + let value = compute_public_data_tree_value(balance - tx_fee); + PublicDataWrite { leaf_slot, value } + } + + fn insert_fee_public_data_write(self, fee_write: PublicDataWrite) -> AppendOnlyTreeSnapshot { + indexed_tree::insert::<_, _, PUBLIC_DATA_TREE_HEIGHT>( + self.start.public_data_tree, + PublicDataTreeLeaf { slot: fee_write.leaf_slot, value: fee_write.value }, + self.fee_write_low_leaf_preimage, + self.fee_write_low_leaf_membership_witness, + self.fee_write_sibling_path, + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { + // Is valid low preimage + let is_update = low_preimage.slot == write.slot; + let is_low_empty = low_preimage.is_empty(); + + let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); + let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); + let is_in_range = is_less_than_slot + & ( + is_next_greater_than + | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0)) + ); + + (!is_low_empty) & (is_update | is_in_range) + }, + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { + // Update low leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage { + slot: low_preimage.slot, + value: write.value, + next_slot: low_preimage.next_slot, + next_index: low_preimage.next_index, + } + } else { + PublicDataTreeLeafPreimage { + slot: low_preimage.slot, + value: low_preimage.value, + next_slot: write.slot, + next_index: write_index, + } + } + }, + |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { + // Build insertion leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage::empty() + } else { + PublicDataTreeLeafPreimage { + slot: write.slot, + value: write.value, + next_slot: low_preimage.next_slot, + next_index: low_preimage.next_index, + } + } + }, + ) + } + + // Check that the block header used by each kernel is a member of the blocks tree --> since the block header + // contains roots of all the trees this is sufficient to verify that the tree roots used by kernels are correct + fn perform_archive_membership_checks(self) { + // For each of the block header (their block hashes), we need to do an inclusion proof + // against the blocks tree root from the beginning of a rollup provided in the rollup constants + let archive_root = self.constants.last_archive.root; + + // Rebuild the block hash + let header = self.tube_data.public_inputs.constants.historical_header; + let previous_block_hash = header.hash(); + + let previous_block_hash_witness = self.archive_root_membership_witness; + + // Now check that the previous block hash is in the blocks tree from the beginning of the rollup + assert_check_membership( + previous_block_hash, + previous_block_hash_witness.leaf_index, + previous_block_hash_witness.sibling_path, + archive_root, + ); + } +} + +fn compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer: AztecAddress) -> Field { + let balances_slot_in_fee_juice_contract = 1; + let fee_payer_balance_slot_in_fee_juice_contract = + derive_storage_slot_in_map(balances_slot_in_fee_juice_contract, fee_payer); + compute_public_data_tree_index( + FEE_JUICE_ADDRESS, + fee_payer_balance_slot_in_fee_juice_contract, + ) } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 89c7410bfd6..28309853a45 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -1,29 +1,51 @@ use crate::{ abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, + base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, constant_rollup_data::ConstantRollupData, }, base::{ - base_rollup_inputs::{BaseRollupInputs, KernelData}, components::{avm_proof_data::AvmProofData, public_tube_data::PublicTubeData}, state_diff_hints::StateDiffHints, }, + components::{compute_kernel_out_hash, compute_tx_effects_hash}, }; use dep::types::{ abis::{ accumulated_data::CombinedAccumulatedData, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, combined_constant_data::CombinedConstantData, + kernel_circuit_public_inputs::KernelCircuitPublicInputs, log_hash::{LogHash, ScopedLogHash}, + nullifier_leaf_preimage::NullifierLeafPreimage, + public_data_write::PublicDataWrite, }, + address::AztecAddress, constants::{ - ARCHIVE_HEIGHT, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_TREE_HEIGHT, - TUBE_VK_INDEX, + ARCHIVE_HEIGHT, FEE_JUICE_ADDRESS, MAX_NOTE_HASHES_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + NOTE_HASH_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, + PRIVATE_KERNEL_EMPTY_INDEX, PUBLIC_DATA_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, }, - data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, - KernelCircuitPublicInputs, - merkle_tree::MembershipWitness, + data::{ + hash::{compute_public_data_tree_index, compute_public_data_tree_value}, + public_data_hint::PublicDataHint, + PublicDataTreeLeaf, + PublicDataTreeLeafPreimage, + }, + hash::silo_l2_to_l1_message, + merkle_tree::{ + append_only_tree, assert_check_membership, calculate_empty_tree_root, + calculate_subtree_root, indexed_tree, MembershipWitness, + }, + messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, - utils::arrays::array_merge, + storage::map::derive_storage_slot_in_map, + traits::is_empty, + utils::{ + arrays::{array_merge, find_index_hint}, + field::{full_field_greater_than, full_field_less_than}, + }, }; pub struct PublicBaseRollupInputs { @@ -127,19 +149,398 @@ impl PublicBaseRollupInputs { // so that we don't have to modify base_rollup_inputs. let public_inputs = self.generate_kernel_circuit_public_inputs(); - BaseRollupInputs { - kernel_data: KernelData { public_inputs }, - start: self.start, - state_diff_hints: self.state_diff_hints, - transaction_fee: self.avm_proof_data.public_inputs.transaction_fee, - fee_payer_fee_juice_balance_read_hint: self.fee_payer_fee_juice_balance_read_hint, - sorted_public_data_writes: self.sorted_public_data_writes, - sorted_public_data_writes_indexes: self.sorted_public_data_writes_indexes, - low_public_data_writes_preimages: self.low_public_data_writes_preimages, - low_public_data_writes_witnesses: self.low_public_data_writes_witnesses, - archive_root_membership_witness: self.archive_root_membership_witness, + // Verify the kernel chain_id and versions + assert( + public_inputs.constants.tx_context.chain_id == self.constants.global_variables.chain_id, + "kernel chain_id does not match the rollup chain_id", + ); + assert( + public_inputs.constants.tx_context.version == self.constants.global_variables.version, + "kernel version does not match the rollup version", + ); + assert( + public_inputs.constants.vk_tree_root == self.constants.vk_tree_root, + "kernel vk_tree_root does not match the rollup vk_tree_root", + ); + assert( + public_inputs.constants.protocol_contract_tree_root + == self.constants.protocol_contract_tree_root, + "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", + ); + + // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. + // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? + assert( + public_inputs.constants.global_variables.is_empty() + | (public_inputs.constants.global_variables == self.constants.global_variables), + "kernel global variables do not match the rollup global variables", + ); + + self.validate_kernel_start_state(public_inputs); + + let rollup_validation_requests = public_inputs.rollup_validation_requests; + + // Verify the max block number + // TODO #5345: why is block_number a Field and not u32? + if rollup_validation_requests.max_block_number.is_some() { + assert( + self.constants.global_variables.block_number as u32 + <= rollup_validation_requests.max_block_number.unwrap_unchecked(), + "kernel max_block_number is smaller than block number", + ); + } + + let commitments_tree_subroot = self.calculate_commitments_subtree(public_inputs); + + let empty_commitments_subtree_root = calculate_empty_tree_root(NOTE_HASH_SUBTREE_HEIGHT); + + let end_note_hash_tree_snapshot = append_only_tree::insert_subtree_to_snapshot_tree( + self.start.note_hash_tree, + self.state_diff_hints.note_hash_subtree_sibling_path, + empty_commitments_subtree_root, + commitments_tree_subroot, + NOTE_HASH_SUBTREE_HEIGHT as u8, + ); + + // Insert nullifiers: + let end_nullifier_tree_snapshot = + self.check_nullifier_tree_non_membership_and_insert_to_tree(public_inputs); + + // Inject protocol update requests for deducting tx_fee from fee_payer's balance + let all_public_data_update_requests = self.calculate_all_public_data_update_requests( + self.avm_proof_data.public_inputs.transaction_fee, + public_inputs, + ); + + // Validate public data update requests and update public data tree + let end_public_data_tree_snapshot = + self.validate_and_process_public_state(all_public_data_update_requests); + + // Calculate the tx effects hash of the transaction + let siloed_l2_to_l1_msgs = public_inputs.end.l2_to_l1_msgs.map( + |message: ScopedL2ToL1Message| silo_l2_to_l1_message( + message, + public_inputs.constants.tx_context.version, + public_inputs.constants.tx_context.chain_id, + ), + ); + let out_hash = compute_kernel_out_hash(siloed_l2_to_l1_msgs); + let tx_effects_hash = compute_tx_effects_hash( + public_inputs.end, + public_inputs.revert_code, + self.avm_proof_data.public_inputs.transaction_fee, + all_public_data_update_requests, + out_hash, + ); + + // Perform membership checks that the notes provided exist within the historical trees data + self.perform_archive_membership_checks(public_inputs); + + BaseOrMergeRollupPublicInputs { + rollup_type: BASE_ROLLUP_TYPE, + num_txs: 1, constants: self.constants, + start: self.start, + end: PartialStateReference { + note_hash_tree: end_note_hash_tree_snapshot, + nullifier_tree: end_nullifier_tree_snapshot, + public_data_tree: end_public_data_tree_snapshot, + }, + txs_effects_hash: tx_effects_hash, + out_hash, + accumulated_fees: self.avm_proof_data.public_inputs.transaction_fee, } - .base_rollup_circuit() } + + // TODO(Kev): This should say calculate_commitments_subtree_root + // Cpp code says calculate_commitments_subtree, so I'm leaving it as is for now + fn calculate_commitments_subtree(self, public_inputs: KernelCircuitPublicInputs) -> Field { + calculate_subtree_root(public_inputs.end.note_hashes) + } + + fn check_nullifier_tree_non_membership_and_insert_to_tree( + self, + public_inputs: KernelCircuitPublicInputs, + ) -> AppendOnlyTreeSnapshot { + indexed_tree::batch_insert( + self.start.nullifier_tree, + public_inputs.end.nullifiers, + self.state_diff_hints.sorted_nullifiers, + self.state_diff_hints.sorted_nullifier_indexes, + self.state_diff_hints.nullifier_subtree_sibling_path, + self.state_diff_hints.nullifier_predecessor_preimages, + self.state_diff_hints.nullifier_predecessor_membership_witnesses.map( + |witness: MembershipWitness| { + MembershipWitness { + leaf_index: witness.leaf_index, + sibling_path: witness.sibling_path, + } + }, + ), + |low_leaf: NullifierLeafPreimage, nullifier: Field| { + // Is valid low leaf + let is_less_than_nullifier = full_field_less_than(low_leaf.nullifier, nullifier); + let is_next_greater_than = full_field_less_than(nullifier, low_leaf.next_nullifier); + + (!low_leaf.is_empty()) + & is_less_than_nullifier + & ( + is_next_greater_than + | ((low_leaf.next_index == 0) & (low_leaf.next_nullifier == 0)) + ) + }, + |low_leaf: NullifierLeafPreimage, nullifier: Field, nullifier_index: u32| { + // Update low leaf + NullifierLeafPreimage { + nullifier: low_leaf.nullifier, + next_nullifier: nullifier, + next_index: nullifier_index, + } + }, + |nullifier: Field, low_leaf: NullifierLeafPreimage| { + // Build insertion leaf + NullifierLeafPreimage { + nullifier: nullifier, + next_nullifier: low_leaf.next_nullifier, + next_index: low_leaf.next_index, + } + }, + [0; NULLIFIER_SUBTREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT], + ) + } + + fn create_nullifier_subtree(leaves: [NullifierLeafPreimage; N]) -> Field { + calculate_subtree_root(leaves.map(|leaf: NullifierLeafPreimage| leaf.hash())) + } + + fn validate_kernel_start_state(self, public_inputs: KernelCircuitPublicInputs) { + let kernel_state = public_inputs.start_state; + if !is_empty(kernel_state) { + assert( + kernel_state.note_hash_tree.eq(self.start.note_hash_tree), + "Mismatch start state for note hash tree", + ); + assert( + kernel_state.nullifier_tree.eq(self.start.nullifier_tree), + "Mismatch start state for nullifier tree", + ); + assert( + kernel_state.public_data_tree.eq(self.start.public_data_tree), + "Mismatch start state for public data tree", + ); + } + } + + fn validate_and_process_public_state( + self, + all_update_requests: [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + ) -> AppendOnlyTreeSnapshot { + let end_public_data_tree_snapshot = insert_public_data_update_requests( + self.start.public_data_tree, + all_update_requests.map(|w: PublicDataWrite| { + PublicDataTreeLeaf { slot: w.leaf_slot, value: w.value } + }), + self.sorted_public_data_writes, + self.sorted_public_data_writes_indexes, + self.low_public_data_writes_preimages, + self.low_public_data_writes_witnesses, + self.state_diff_hints.public_data_sibling_path, + ); + + end_public_data_tree_snapshot + } + + // Returns an array with all public data update requests for this tx. This includes all update requests + // generated by app circuits, plus the protocol update requests injected by this circuit. The only protocol + // update request we have at the time of this writing is deducting the tx_fee from the fee_payer balance. + fn calculate_all_public_data_update_requests( + self, + tx_fee: Field, + public_inputs: KernelCircuitPublicInputs, + ) -> [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] { + let mut all_update_requests: [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = + [PublicDataWrite::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; + for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { + all_update_requests[i] = public_inputs.end.public_data_writes[i]; + } + + let (payment_update_request, payment_update_index) = + self.build_or_patch_payment_update_request(tx_fee, public_inputs); + all_update_requests[payment_update_index] = payment_update_request; + + all_update_requests + } + + // Deducts the tx_fee from the FeeJuice balance of the fee_payer. If there is already a PublicDataUpdateRequest + // in this tx for their balance (because they issued a 'claim' to increase their balance by bridging from L1), + // update it by subtracting the tx_fee. Otherwise, build a new PublicDataUpdateRequest to subtract the tx_fee + // from the balance of the fee_payer, using the fee_payer_fee_juice_balance_read_hint to read the current balance. + // Returns the data update request that subtracts the tx_fee from the fee_payer's balance, and the index where it + // should be inserted in the public data update requests array. + fn build_or_patch_payment_update_request( + self, + tx_fee: Field, + public_inputs: KernelCircuitPublicInputs, + ) -> (PublicDataWrite, u32) { + let fee_payer = public_inputs.fee_payer; + + // TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx + if !fee_payer.is_zero() { + let read_hint = self.fee_payer_fee_juice_balance_read_hint; + let leaf_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + let existing_update_index = unsafe { + find_index_hint( + public_inputs.end.public_data_writes, + |w: PublicDataWrite| w.leaf_slot == leaf_slot, + ) + }; + + if existing_update_index != MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { + // Is there a balance update already in this tx? If so, update it and return its index. + let existing_update = public_inputs.end.public_data_writes[existing_update_index]; + assert( + existing_update.leaf_slot == leaf_slot, + "Wrong leaf slot for Fee Juice balance update request", + ); + assert( + !existing_update.value.lt(tx_fee), + "Not enough balance for fee payer after claim to pay for transaction", + ); + + let value = compute_public_data_tree_value(existing_update.value - tx_fee); + let protocol_update_request = PublicDataWrite { leaf_slot, value }; + (protocol_update_request, existing_update_index as u32) + } else { + // Otherwise, build a new one to be inserted into the protocol update requests at the end of the array. + read_hint.validate(self.start.public_data_tree.root); + + let balance = read_hint.value; + assert( + read_hint.leaf_slot == leaf_slot, + "Wrong leaf slot for Fee Juice balance read hint", + ); + assert( + !balance.lt(tx_fee), + "Not enough balance for fee payer to pay for transaction", + ); + + let value = compute_public_data_tree_value(balance - tx_fee); + let protocol_update_request = PublicDataWrite { leaf_slot, value }; + (protocol_update_request, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) + } + } else { + // Nothing to do, just place an empty update request at the end of the array + (PublicDataWrite::empty(), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) + } + } + + // Check that the block header used by each kernel is a member of the blocks tree --> since the block header + // contains roots of all the trees this is sufficient to verify that the tree roots used by kernels are correct + fn perform_archive_membership_checks(self, public_inputs: KernelCircuitPublicInputs) { + // For each of the block header (their block hashes), we need to do an inclusion proof + // against the blocks tree root from the beginning of a rollup provided in the rollup constants + let archive_root = self.constants.last_archive.root; + + // Rebuild the block hash + let header = public_inputs.constants.historical_header; + let previous_block_hash = header.hash(); + + let previous_block_hash_witness = self.archive_root_membership_witness; + + // Now check that the previous block hash is in the blocks tree from the beginning of the rollup + assert_check_membership( + previous_block_hash, + previous_block_hash_witness.leaf_index, + previous_block_hash_witness.sibling_path, + archive_root, + ); + } +} + +fn insert_public_data_update_requests( + prev_snapshot: AppendOnlyTreeSnapshot, + public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_writes_subtree_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], +) -> AppendOnlyTreeSnapshot { + indexed_tree::batch_insert( + prev_snapshot, + public_data_writes, + sorted_public_data_writes, + sorted_public_data_writes_indexes, + public_data_writes_subtree_sibling_path, + low_public_data_writes_preimages, + low_public_data_writes_witnesses.map( + |witness: MembershipWitness| { + MembershipWitness { + leaf_index: witness.leaf_index, + sibling_path: witness.sibling_path, + } + }, + ), + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { + // Is valid low preimage + let is_update = low_preimage.slot == write.slot; + let is_low_empty = low_preimage.is_empty(); + + let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); + let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); + let is_in_range = is_less_than_slot + & ( + is_next_greater_than + | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0)) + ); + + (!is_low_empty) & (is_update | is_in_range) + }, + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { + // Update low leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage { + slot: low_preimage.slot, + value: write.value, + next_slot: low_preimage.next_slot, + next_index: low_preimage.next_index, + } + } else { + PublicDataTreeLeafPreimage { + slot: low_preimage.slot, + value: low_preimage.value, + next_slot: write.slot, + next_index: write_index, + } + } + }, + |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { + // Build insertion leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage::empty() + } else { + PublicDataTreeLeafPreimage { + slot: write.slot, + value: write.value, + next_slot: low_preimage.next_slot, + next_index: low_preimage.next_index, + } + } + }, + [0; PUBLIC_DATA_SUBTREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT], + ) +} + +fn compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer: AztecAddress) -> Field { + let balances_slot_in_fee_juice_contract = 1; + let fee_payer_balance_slot_in_fee_juice_contract = + derive_storage_slot_in_map(balances_slot_in_fee_juice_contract, fee_payer); + compute_public_data_tree_index( + FEE_JUICE_ADDRESS, + fee_payer_balance_slot_in_fee_juice_contract, + ) } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr index f0cc741a909..6fc3b405218 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr @@ -29,6 +29,8 @@ where Leaf: Hash + Empty, { // A permutation to the values is provided to make the insertion use only one insertion strategy + // However, for the actual insertion in the tree the original order is respected, the sorting is only used for validation of the links + // and low leaf updates. check_permutation(values_to_insert, sorted_values, sorted_values_indexes); // Now, update the existing leaves with the new leaves @@ -98,3 +100,59 @@ where next_available_leaf_index: start_insertion_index + (values_to_insert.len() as u32), } } + +pub fn insert( + mut snapshot: AppendOnlyTreeSnapshot, + value: Value, + low_leaf_preimage: Leaf, + low_leaf_membership_witness: MembershipWitness, + insertion_sibling_path: [Field; TreeHeight], + is_valid_low_leaf: fn(Leaf, Value) -> bool, + update_low_leaf: fn(Leaf, Value, u32) -> Leaf, + build_insertion_leaf: fn(Value, Leaf) -> Leaf, +) -> AppendOnlyTreeSnapshot +where + Value: Eq + Empty, + Leaf: Hash + Empty, +{ + if !is_empty(value) { + assert(is_valid_low_leaf(low_leaf_preimage, value), "Invalid low leaf"); + + // perform membership check for the low leaf against the original root + assert_check_membership( + low_leaf_preimage.hash(), + low_leaf_membership_witness.leaf_index, + low_leaf_membership_witness.sibling_path, + snapshot.root, + ); + + // Calculate the new value of the low_leaf + let updated_low_leaf = + update_low_leaf(low_leaf_preimage, value, snapshot.next_available_leaf_index); + + snapshot.root = root_from_sibling_path( + updated_low_leaf.hash(), + low_leaf_membership_witness.leaf_index, + low_leaf_membership_witness.sibling_path, + ); + + let insertion_leaf = build_insertion_leaf(value, low_leaf_preimage); + + assert_check_membership( + 0, + snapshot.next_available_leaf_index as Field, + insertion_sibling_path, + snapshot.root, + ); + + // Calculate the new root + snapshot.root = root_from_sibling_path( + insertion_leaf.hash(), + snapshot.next_available_leaf_index as Field, + insertion_sibling_path, + ); + + snapshot.next_available_leaf_index += 1; + } + snapshot +} From 385949788d045ef1a186badc5879f5615f6390f2 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 14 Nov 2024 11:50:26 +0000 Subject: [PATCH 02/25] update public data write strategy --- .../rollup-lib/src/base/components/fees.nr | 14 ++ .../rollup-lib/src/base/components/mod.nr | 3 + .../src/base/components/nullifier_tree.nr | 70 +++++++ .../src/base/components/public_data_tree.nr | 76 +++++++ .../src/base/private_base_rollup.nr | 168 ++++----------- .../rollup-lib/src/base/public_base_rollup.nr | 197 +++--------------- .../rollup-lib/src/base/state_diff_hints.nr | 34 ++- .../types/src/merkle_tree/indexed_tree.nr | 77 ++++--- .../src/structs/rollup/base_rollup_hints.ts | 107 +++++++++- .../src/type_conversion.ts | 14 -- 10 files changed, 393 insertions(+), 367 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/fees.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_data_tree.nr diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/fees.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/fees.nr new file mode 100644 index 00000000000..f47a0f1e7b3 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/fees.nr @@ -0,0 +1,14 @@ +use dep::types::{ + address::AztecAddress, constants::FEE_JUICE_ADDRESS, data::hash::compute_public_data_tree_index, + storage::map::derive_storage_slot_in_map, +}; + +pub(crate) fn compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer: AztecAddress) -> Field { + let balances_slot_in_fee_juice_contract = 1; + let fee_payer_balance_slot_in_fee_juice_contract = + derive_storage_slot_in_map(balances_slot_in_fee_juice_contract, fee_payer); + compute_public_data_tree_index( + FEE_JUICE_ADDRESS, + fee_payer_balance_slot_in_fee_juice_contract, + ) +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr index c998111d9a6..352897aa409 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr @@ -1,3 +1,6 @@ pub mod avm_proof_data; pub mod private_tube_data; pub mod public_tube_data; +pub(crate) mod public_data_tree; +pub(crate) mod nullifier_tree; +pub(crate) mod fees; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr new file mode 100644 index 00000000000..ccda4d8c448 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr @@ -0,0 +1,70 @@ +use dep::types::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + nullifier_leaf_preimage::NullifierLeafPreimage, + }, + constants::{ + MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, + NULLIFIER_TREE_HEIGHT, + }, + data, + merkle_tree::{indexed_tree, MembershipWitness}, + utils::field::full_field_less_than, +}; + +pub(crate) fn nullifier_tree_batch_insert( + start_snapshot: AppendOnlyTreeSnapshot, + nullifiers: [Field; MAX_NULLIFIERS_PER_TX], + sorted_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], + sorted_nullifiers_indexes: [u32; MAX_NULLIFIERS_PER_TX], + nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], + nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], +) -> AppendOnlyTreeSnapshot { + indexed_tree::batch_insert( + start_snapshot, + nullifiers, + sorted_nullifiers, + sorted_nullifiers_indexes, + nullifier_subtree_sibling_path, + nullifier_predecessor_preimages, + nullifier_predecessor_membership_witnesses.map( + |witness: MembershipWitness| { + MembershipWitness { + leaf_index: witness.leaf_index, + sibling_path: witness.sibling_path, + } + }, + ), + |low_leaf: NullifierLeafPreimage, nullifier: Field| { + // Is valid low leaf + let is_less_than_nullifier = full_field_less_than(low_leaf.nullifier, nullifier); + let is_next_greater_than = full_field_less_than(nullifier, low_leaf.next_nullifier); + + (!low_leaf.is_empty()) + & is_less_than_nullifier + & ( + is_next_greater_than + | ((low_leaf.next_index == 0) & (low_leaf.next_nullifier == 0)) + ) + }, + |low_leaf: NullifierLeafPreimage, nullifier: Field, nullifier_index: u32| { + // Update low leaf + NullifierLeafPreimage { + nullifier: low_leaf.nullifier, + next_nullifier: nullifier, + next_index: nullifier_index, + } + }, + |nullifier: Field, low_leaf: NullifierLeafPreimage| { + // Build insertion leaf + NullifierLeafPreimage { + nullifier: nullifier, + next_nullifier: low_leaf.next_nullifier, + next_index: low_leaf.next_index, + } + }, + [0; NULLIFIER_SUBTREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT], + ) +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_data_tree.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_data_tree.nr new file mode 100644 index 00000000000..e3d152ba98d --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_data_tree.nr @@ -0,0 +1,76 @@ +use dep::types::{ + abis::{append_only_tree_snapshot::AppendOnlyTreeSnapshot, public_data_write::PublicDataWrite}, + constants::PUBLIC_DATA_TREE_HEIGHT, + data::{PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, + merkle_tree::{indexed_tree, MembershipWitness}, + traits::is_empty, + utils::field::full_field_less_than, +}; + +pub(crate) fn public_data_tree_insert( + write: PublicDataWrite, + start_snapshot: AppendOnlyTreeSnapshot, + low_leaf_preimage: PublicDataTreeLeafPreimage, + low_leaf_membership_witness: MembershipWitness, + sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT], +) -> AppendOnlyTreeSnapshot { + if !is_empty(write) { + indexed_tree::insert::<_, _, PUBLIC_DATA_TREE_HEIGHT>( + start_snapshot, + PublicDataTreeLeaf { slot: write.leaf_slot, value: write.value }, + low_leaf_preimage, + low_leaf_membership_witness, + sibling_path, + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { + // Is valid low preimage + let is_update = low_preimage.slot == write.slot; + let is_low_empty = low_preimage.is_empty(); + + let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); + let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); + let is_in_range = is_less_than_slot + & ( + is_next_greater_than + | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0)) + ); + + (!is_low_empty) & (is_update | is_in_range) + }, + |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { + // Update low leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage { + slot: low_preimage.slot, + value: write.value, + next_slot: low_preimage.next_slot, + next_index: low_preimage.next_index, + } + } else { + PublicDataTreeLeafPreimage { + slot: low_preimage.slot, + value: low_preimage.value, + next_slot: write.slot, + next_index: write_index, + } + } + }, + |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { + // Build insertion leaf + let is_update = low_preimage.slot == write.slot; + if is_update { + PublicDataTreeLeafPreimage::empty() + } else { + PublicDataTreeLeafPreimage { + slot: write.slot, + value: write.value, + next_slot: low_preimage.next_slot, + next_index: low_preimage.next_index, + } + } + }, + ) + } else { + start_snapshot + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 85a74086ea6..43990f50b52 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -3,39 +3,34 @@ use crate::{ base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, constant_rollup_data::ConstantRollupData, }, - base::{components::private_tube_data::PrivateTubeData, state_diff_hints::StateDiffHints}, + base::{ + components::{ + fees::compute_fee_payer_fee_juice_balance_leaf_slot, + nullifier_tree::nullifier_tree_batch_insert, private_tube_data::PrivateTubeData, + public_data_tree::public_data_tree_insert, + }, + state_diff_hints::PrivateBaseStateDiffHints, + }, components::{compute_kernel_out_hash, compute_tx_effects_hash}, }; use dep::types::{ abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, - kernel_circuit_public_inputs::KernelCircuitPublicInputs, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, }, - address::AztecAddress, constants::{ - ARCHIVE_HEIGHT, FEE_JUICE_ADDRESS, MAX_NOTE_HASHES_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - NOTE_HASH_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, - PRIVATE_KERNEL_EMPTY_INDEX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, - }, - data::{ - hash::{compute_public_data_tree_index, compute_public_data_tree_value}, - public_data_hint::PublicDataHint, - PublicDataTreeLeaf, - PublicDataTreeLeafPreimage, + ARCHIVE_HEIGHT, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, + PRIVATE_KERNEL_EMPTY_INDEX, TUBE_VK_INDEX, }, + data::{hash::compute_public_data_tree_value, public_data_hint::PublicDataHint}, hash::silo_l2_to_l1_message, merkle_tree::{ append_only_tree, assert_check_membership, calculate_empty_tree_root, - calculate_subtree_root, indexed_tree, MembershipWitness, + calculate_subtree_root, MembershipWitness, }, messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, - storage::map::derive_storage_slot_in_map, traits::is_empty, - utils::{arrays::find_index_hint, field::{full_field_greater_than, full_field_less_than}}, }; global ALLOWED_PREVIOUS_CIRCUITS = [PRIVATE_KERNEL_EMPTY_INDEX, TUBE_VK_INDEX]; @@ -45,13 +40,9 @@ pub struct PrivateBaseRollupInputs { start: PartialStateReference, - state_diff_hints: StateDiffHints, + state_diff_hints: PrivateBaseStateDiffHints, fee_payer_fee_juice_balance_read_hint: PublicDataHint, - fee_write_low_leaf_preimage: PublicDataTreeLeafPreimage, - fee_write_low_leaf_membership_witness: MembershipWitness, - fee_write_sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT], - archive_root_membership_witness: MembershipWitness, constants: ConstantRollupData, } @@ -185,51 +176,14 @@ impl PrivateBaseRollupInputs { } fn check_nullifier_tree_non_membership_and_insert_to_tree(self) -> AppendOnlyTreeSnapshot { - indexed_tree::batch_insert( + nullifier_tree_batch_insert( self.start.nullifier_tree, self.tube_data.public_inputs.end.nullifiers, self.state_diff_hints.sorted_nullifiers, self.state_diff_hints.sorted_nullifier_indexes, self.state_diff_hints.nullifier_subtree_sibling_path, self.state_diff_hints.nullifier_predecessor_preimages, - self.state_diff_hints.nullifier_predecessor_membership_witnesses.map( - |witness: MembershipWitness| { - MembershipWitness { - leaf_index: witness.leaf_index, - sibling_path: witness.sibling_path, - } - }, - ), - |low_leaf: NullifierLeafPreimage, nullifier: Field| { - // Is valid low leaf - let is_less_than_nullifier = full_field_less_than(low_leaf.nullifier, nullifier); - let is_next_greater_than = full_field_less_than(nullifier, low_leaf.next_nullifier); - - (!low_leaf.is_empty()) - & is_less_than_nullifier - & ( - is_next_greater_than - | ((low_leaf.next_index == 0) & (low_leaf.next_nullifier == 0)) - ) - }, - |low_leaf: NullifierLeafPreimage, nullifier: Field, nullifier_index: u32| { - // Update low leaf - NullifierLeafPreimage { - nullifier: low_leaf.nullifier, - next_nullifier: nullifier, - next_index: nullifier_index, - } - }, - |nullifier: Field, low_leaf: NullifierLeafPreimage| { - // Build insertion leaf - NullifierLeafPreimage { - nullifier: nullifier, - next_nullifier: low_leaf.next_nullifier, - next_index: low_leaf.next_index, - } - }, - [0; NULLIFIER_SUBTREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT], + self.state_diff_hints.nullifier_predecessor_membership_witnesses, ) } @@ -258,75 +212,34 @@ impl PrivateBaseRollupInputs { fn build_fee_public_data_write(self, tx_fee: Field) -> PublicDataWrite { let fee_payer = self.tube_data.public_inputs.fee_payer; - let read_hint = self.fee_payer_fee_juice_balance_read_hint; - let leaf_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + if fee_payer.is_zero() { + PublicDataWrite::empty() + } else { + let read_hint = self.fee_payer_fee_juice_balance_read_hint; + let leaf_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - // Otherwise, build a new one to be inserted into the protocol update requests at the end of the array. - read_hint.validate(self.start.public_data_tree.root); + // Otherwise, build a new one to be inserted into the protocol update requests at the end of the array. + read_hint.validate(self.start.public_data_tree.root); - let balance = read_hint.value; - assert(read_hint.leaf_slot == leaf_slot, "Wrong leaf slot for Fee Juice balance read hint"); - assert(!balance.lt(tx_fee), "Not enough balance for fee payer to pay for transaction"); + let balance = read_hint.value; + assert( + read_hint.leaf_slot == leaf_slot, + "Wrong leaf slot for Fee Juice balance read hint", + ); + assert(!balance.lt(tx_fee), "Not enough balance for fee payer to pay for transaction"); - let value = compute_public_data_tree_value(balance - tx_fee); - PublicDataWrite { leaf_slot, value } + let value = compute_public_data_tree_value(balance - tx_fee); + PublicDataWrite { leaf_slot, value } + } } fn insert_fee_public_data_write(self, fee_write: PublicDataWrite) -> AppendOnlyTreeSnapshot { - indexed_tree::insert::<_, _, PUBLIC_DATA_TREE_HEIGHT>( + public_data_tree_insert( + fee_write, self.start.public_data_tree, - PublicDataTreeLeaf { slot: fee_write.leaf_slot, value: fee_write.value }, - self.fee_write_low_leaf_preimage, - self.fee_write_low_leaf_membership_witness, - self.fee_write_sibling_path, - |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { - // Is valid low preimage - let is_update = low_preimage.slot == write.slot; - let is_low_empty = low_preimage.is_empty(); - - let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); - let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); - let is_in_range = is_less_than_slot - & ( - is_next_greater_than - | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0)) - ); - - (!is_low_empty) & (is_update | is_in_range) - }, - |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { - // Update low leaf - let is_update = low_preimage.slot == write.slot; - if is_update { - PublicDataTreeLeafPreimage { - slot: low_preimage.slot, - value: write.value, - next_slot: low_preimage.next_slot, - next_index: low_preimage.next_index, - } - } else { - PublicDataTreeLeafPreimage { - slot: low_preimage.slot, - value: low_preimage.value, - next_slot: write.slot, - next_index: write_index, - } - } - }, - |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { - // Build insertion leaf - let is_update = low_preimage.slot == write.slot; - if is_update { - PublicDataTreeLeafPreimage::empty() - } else { - PublicDataTreeLeafPreimage { - slot: write.slot, - value: write.value, - next_slot: low_preimage.next_slot, - next_index: low_preimage.next_index, - } - } - }, + self.state_diff_hints.fee_write_low_leaf_preimage, + self.state_diff_hints.fee_write_low_leaf_membership_witness, + self.state_diff_hints.fee_write_sibling_path, ) } @@ -353,12 +266,3 @@ impl PrivateBaseRollupInputs { } } -fn compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer: AztecAddress) -> Field { - let balances_slot_in_fee_juice_contract = 1; - let fee_payer_balance_slot_in_fee_juice_contract = - derive_storage_slot_in_map(balances_slot_in_fee_juice_contract, fee_payer); - compute_public_data_tree_index( - FEE_JUICE_ADDRESS, - fee_payer_balance_slot_in_fee_juice_contract, - ) -} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 28309853a45..36ce3b8a4cf 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -4,8 +4,12 @@ use crate::{ constant_rollup_data::ConstantRollupData, }, base::{ - components::{avm_proof_data::AvmProofData, public_tube_data::PublicTubeData}, - state_diff_hints::StateDiffHints, + components::{ + avm_proof_data::AvmProofData, fees::compute_fee_payer_fee_juice_balance_leaf_slot, + nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, + public_tube_data::PublicTubeData, + }, + state_diff_hints::PublicBaseStateDiffHints, }, components::{compute_kernel_out_hash, compute_tx_effects_hash}, }; @@ -19,33 +23,20 @@ use dep::types::{ nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, }, - address::AztecAddress, constants::{ - ARCHIVE_HEIGHT, FEE_JUICE_ADDRESS, MAX_NOTE_HASHES_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - NOTE_HASH_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, - PRIVATE_KERNEL_EMPTY_INDEX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, - }, - data::{ - hash::{compute_public_data_tree_index, compute_public_data_tree_value}, - public_data_hint::PublicDataHint, - PublicDataTreeLeaf, - PublicDataTreeLeafPreimage, + ARCHIVE_HEIGHT, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, }, + data::{hash::compute_public_data_tree_value, public_data_hint::PublicDataHint}, hash::silo_l2_to_l1_message, merkle_tree::{ append_only_tree, assert_check_membership, calculate_empty_tree_root, - calculate_subtree_root, indexed_tree, MembershipWitness, + calculate_subtree_root, MembershipWitness, }, messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, - storage::map::derive_storage_slot_in_map, traits::is_empty, - utils::{ - arrays::{array_merge, find_index_hint}, - field::{full_field_greater_than, full_field_less_than}, - }, + utils::arrays::{array_merge, find_index_hint}, }; pub struct PublicBaseRollupInputs { @@ -54,14 +45,9 @@ pub struct PublicBaseRollupInputs { start: PartialStateReference, - state_diff_hints: StateDiffHints, + state_diff_hints: PublicBaseStateDiffHints, fee_payer_fee_juice_balance_read_hint: PublicDataHint, - sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - archive_root_membership_witness: MembershipWitness, constants: ConstantRollupData, } @@ -190,7 +176,7 @@ impl PublicBaseRollupInputs { ); } - let commitments_tree_subroot = self.calculate_commitments_subtree(public_inputs); + let commitments_tree_subroot = self.calculate_commitments_subtree_root(public_inputs); let empty_commitments_subtree_root = calculate_empty_tree_root(NOTE_HASH_SUBTREE_HEIGHT); @@ -252,9 +238,7 @@ impl PublicBaseRollupInputs { } } - // TODO(Kev): This should say calculate_commitments_subtree_root - // Cpp code says calculate_commitments_subtree, so I'm leaving it as is for now - fn calculate_commitments_subtree(self, public_inputs: KernelCircuitPublicInputs) -> Field { + fn calculate_commitments_subtree_root(self, public_inputs: KernelCircuitPublicInputs) -> Field { calculate_subtree_root(public_inputs.end.note_hashes) } @@ -262,51 +246,14 @@ impl PublicBaseRollupInputs { self, public_inputs: KernelCircuitPublicInputs, ) -> AppendOnlyTreeSnapshot { - indexed_tree::batch_insert( + nullifier_tree_batch_insert( self.start.nullifier_tree, public_inputs.end.nullifiers, self.state_diff_hints.sorted_nullifiers, self.state_diff_hints.sorted_nullifier_indexes, self.state_diff_hints.nullifier_subtree_sibling_path, self.state_diff_hints.nullifier_predecessor_preimages, - self.state_diff_hints.nullifier_predecessor_membership_witnesses.map( - |witness: MembershipWitness| { - MembershipWitness { - leaf_index: witness.leaf_index, - sibling_path: witness.sibling_path, - } - }, - ), - |low_leaf: NullifierLeafPreimage, nullifier: Field| { - // Is valid low leaf - let is_less_than_nullifier = full_field_less_than(low_leaf.nullifier, nullifier); - let is_next_greater_than = full_field_less_than(nullifier, low_leaf.next_nullifier); - - (!low_leaf.is_empty()) - & is_less_than_nullifier - & ( - is_next_greater_than - | ((low_leaf.next_index == 0) & (low_leaf.next_nullifier == 0)) - ) - }, - |low_leaf: NullifierLeafPreimage, nullifier: Field, nullifier_index: u32| { - // Update low leaf - NullifierLeafPreimage { - nullifier: low_leaf.nullifier, - next_nullifier: nullifier, - next_index: nullifier_index, - } - }, - |nullifier: Field, low_leaf: NullifierLeafPreimage| { - // Build insertion leaf - NullifierLeafPreimage { - nullifier: nullifier, - next_nullifier: low_leaf.next_nullifier, - next_index: low_leaf.next_index, - } - }, - [0; NULLIFIER_SUBTREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT], + self.state_diff_hints.nullifier_predecessor_membership_witnesses, ) } @@ -336,19 +283,18 @@ impl PublicBaseRollupInputs { self, all_update_requests: [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], ) -> AppendOnlyTreeSnapshot { - let end_public_data_tree_snapshot = insert_public_data_update_requests( - self.start.public_data_tree, - all_update_requests.map(|w: PublicDataWrite| { - PublicDataTreeLeaf { slot: w.leaf_slot, value: w.value } - }), - self.sorted_public_data_writes, - self.sorted_public_data_writes_indexes, - self.low_public_data_writes_preimages, - self.low_public_data_writes_witnesses, - self.state_diff_hints.public_data_sibling_path, - ); - - end_public_data_tree_snapshot + let mut snapshot = self.start.public_data_tree; + for i in 0..all_update_requests.len() { + let update_request = all_update_requests[i]; + snapshot = public_data_tree_insert( + update_request, + snapshot, + self.state_diff_hints.low_public_data_writes_preimages[i], + self.state_diff_hints.low_public_data_writes_witnesses[i], + self.state_diff_hints.public_data_tree_sibling_paths[i], + ); + } + snapshot } // Returns an array with all public data update requests for this tx. This includes all update requests @@ -457,90 +403,3 @@ impl PublicBaseRollupInputs { ); } } - -fn insert_public_data_update_requests( - prev_snapshot: AppendOnlyTreeSnapshot, - public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_writes_subtree_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], -) -> AppendOnlyTreeSnapshot { - indexed_tree::batch_insert( - prev_snapshot, - public_data_writes, - sorted_public_data_writes, - sorted_public_data_writes_indexes, - public_data_writes_subtree_sibling_path, - low_public_data_writes_preimages, - low_public_data_writes_witnesses.map( - |witness: MembershipWitness| { - MembershipWitness { - leaf_index: witness.leaf_index, - sibling_path: witness.sibling_path, - } - }, - ), - |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { - // Is valid low preimage - let is_update = low_preimage.slot == write.slot; - let is_low_empty = low_preimage.is_empty(); - - let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); - let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); - let is_in_range = is_less_than_slot - & ( - is_next_greater_than - | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0)) - ); - - (!is_low_empty) & (is_update | is_in_range) - }, - |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { - // Update low leaf - let is_update = low_preimage.slot == write.slot; - if is_update { - PublicDataTreeLeafPreimage { - slot: low_preimage.slot, - value: write.value, - next_slot: low_preimage.next_slot, - next_index: low_preimage.next_index, - } - } else { - PublicDataTreeLeafPreimage { - slot: low_preimage.slot, - value: low_preimage.value, - next_slot: write.slot, - next_index: write_index, - } - } - }, - |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { - // Build insertion leaf - let is_update = low_preimage.slot == write.slot; - if is_update { - PublicDataTreeLeafPreimage::empty() - } else { - PublicDataTreeLeafPreimage { - slot: write.slot, - value: write.value, - next_slot: low_preimage.next_slot, - next_index: low_preimage.next_index, - } - } - }, - [0; PUBLIC_DATA_SUBTREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT], - ) -} - -fn compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer: AztecAddress) -> Field { - let balances_slot_in_fee_juice_contract = 1; - let fee_payer_balance_slot_in_fee_juice_contract = - derive_storage_slot_in_map(balances_slot_in_fee_juice_contract, fee_payer); - compute_public_data_tree_index( - FEE_JUICE_ADDRESS, - fee_payer_balance_slot_in_fee_juice_contract, - ) -} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr index 5015f7ed779..2144f1447bb 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr @@ -1,14 +1,15 @@ use dep::types::{ abis::nullifier_leaf_preimage::NullifierLeafPreimage, constants::{ - MAX_NULLIFIERS_PER_TX, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, - NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, + MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, + NULLIFIER_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, }, + data::{PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, merkle_tree::MembershipWitness, }; -pub struct StateDiffHints { +pub struct PrivateBaseStateDiffHints { nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], @@ -20,5 +21,28 @@ pub struct StateDiffHints { // snapshots of the relevant trees are stored in partial state reference). note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], - public_data_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], + + fee_write_low_leaf_preimage: PublicDataTreeLeafPreimage, + fee_write_low_leaf_membership_witness: MembershipWitness, + fee_write_sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT], +} + +pub struct PublicBaseStateDiffHints { + nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], + nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], + + sorted_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], + sorted_nullifier_indexes: [u32; MAX_NULLIFIERS_PER_TX], + + // For inserting the new subtrees into their respective trees: + // Note: the insertion leaf index can be derived from the snapshots' `next_available_leaf_index` values (tree + // snapshots of the relevant trees are stored in partial state reference). + note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], + nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + + sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + public_data_tree_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr index 6fc3b405218..d6d2a363fd3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/indexed_tree.nr @@ -115,44 +115,43 @@ where Value: Eq + Empty, Leaf: Hash + Empty, { - if !is_empty(value) { - assert(is_valid_low_leaf(low_leaf_preimage, value), "Invalid low leaf"); - - // perform membership check for the low leaf against the original root - assert_check_membership( - low_leaf_preimage.hash(), - low_leaf_membership_witness.leaf_index, - low_leaf_membership_witness.sibling_path, - snapshot.root, - ); - - // Calculate the new value of the low_leaf - let updated_low_leaf = - update_low_leaf(low_leaf_preimage, value, snapshot.next_available_leaf_index); - - snapshot.root = root_from_sibling_path( - updated_low_leaf.hash(), - low_leaf_membership_witness.leaf_index, - low_leaf_membership_witness.sibling_path, - ); - - let insertion_leaf = build_insertion_leaf(value, low_leaf_preimage); - - assert_check_membership( - 0, - snapshot.next_available_leaf_index as Field, - insertion_sibling_path, - snapshot.root, - ); - - // Calculate the new root - snapshot.root = root_from_sibling_path( - insertion_leaf.hash(), - snapshot.next_available_leaf_index as Field, - insertion_sibling_path, - ); - - snapshot.next_available_leaf_index += 1; - } + assert(is_valid_low_leaf(low_leaf_preimage, value), "Invalid low leaf"); + + // perform membership check for the low leaf against the original root + assert_check_membership( + low_leaf_preimage.hash(), + low_leaf_membership_witness.leaf_index, + low_leaf_membership_witness.sibling_path, + snapshot.root, + ); + + // Calculate the new value of the low_leaf + let updated_low_leaf = + update_low_leaf(low_leaf_preimage, value, snapshot.next_available_leaf_index); + + snapshot.root = root_from_sibling_path( + updated_low_leaf.hash(), + low_leaf_membership_witness.leaf_index, + low_leaf_membership_witness.sibling_path, + ); + + let insertion_leaf = build_insertion_leaf(value, low_leaf_preimage); + + assert_check_membership( + 0, + snapshot.next_available_leaf_index as Field, + insertion_sibling_path, + snapshot.root, + ); + + // Calculate the new root + snapshot.root = root_from_sibling_path( + insertion_leaf.hash(), + snapshot.next_available_leaf_index as Field, + insertion_sibling_path, + ); + + snapshot.next_available_leaf_index += 1; + snapshot } diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts index 15364ffb636..1eb4cffc331 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts @@ -1,4 +1,5 @@ import { makeTuple } from '@aztec/foundation/array'; +import { Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; @@ -15,7 +16,97 @@ import { PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '../trees/index.j import { ConstantRollupData } from './constant_rollup_data.js'; import { StateDiffHints } from './state_diff_hints.js'; -export class BaseRollupHints { +export type BaseRollupHints = PrivateBaseRollupHints | PublicBaseRollupHints; + +export class PrivateBaseRollupHints { + constructor( + /** Partial state reference at the start of the rollup. */ + public start: PartialStateReference, + /** Hints used while proving state diff validity. */ + public stateDiffHints: StateDiffHints, + /** Public data read hint for accessing the balance of the fee payer. */ + public feePayerFeeJuiceBalanceReadHint: PublicDataHint, + + public feeWriteLowLeafPreimage: PublicDataTreeLeafPreimage, + public feeWriteLowLeafMembershipWitness: MembershipWitness, + public feeWriteSiblingPath: Tuple, + + /** + * Membership witnesses of blocks referred by each of the 2 kernels. + */ + public archiveRootMembershipWitness: MembershipWitness, + /** + * Data which is not modified by the base rollup circuit. + */ + public constants: ConstantRollupData, + ) {} + + static from(fields: FieldsOf): BaseRollupHints { + return new PrivateBaseRollupHints(...PrivateBaseRollupHints.getFields(fields)); + } + + static getFields(fields: FieldsOf) { + return [ + fields.start, + fields.stateDiffHints, + fields.feePayerFeeJuiceBalanceReadHint, + fields.feeWriteLowLeafPreimage, + fields.feeWriteLowLeafMembershipWitness, + fields.feeWriteSiblingPath, + fields.archiveRootMembershipWitness, + fields.constants, + ] as const; + } + + /** + * Serializes the inputs to a buffer. + * @returns The inputs serialized to a buffer. + */ + toBuffer() { + return serializeToBuffer(...PrivateBaseRollupHints.getFields(this)); + } + + /** + * Serializes the inputs to a hex string. + * @returns The instance serialized to a hex string. + */ + toString() { + return this.toBuffer().toString('hex'); + } + + static fromBuffer(buffer: Buffer | BufferReader): BaseRollupHints { + const reader = BufferReader.asReader(buffer); + return new PrivateBaseRollupHints( + reader.readObject(PartialStateReference), + reader.readObject(StateDiffHints), + reader.readObject(PublicDataHint), + reader.readObject(PublicDataTreeLeafPreimage), + MembershipWitness.fromBuffer(reader, PUBLIC_DATA_TREE_HEIGHT), + reader.readArray(PUBLIC_DATA_TREE_HEIGHT, Fr), + MembershipWitness.fromBuffer(reader, ARCHIVE_HEIGHT), + reader.readObject(ConstantRollupData), + ); + } + + static fromString(str: string) { + return PrivateBaseRollupHints.fromBuffer(Buffer.from(str, 'hex')); + } + + static empty() { + return new PrivateBaseRollupHints( + PartialStateReference.empty(), + StateDiffHints.empty(), + PublicDataHint.empty(), + PublicDataTreeLeafPreimage.empty(), + MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT), + makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero), + MembershipWitness.empty(ARCHIVE_HEIGHT), + ConstantRollupData.empty(), + ); + } +} + +export class PublicBaseRollupHints { constructor( /** Partial state reference at the start of the rollup. */ public start: PartialStateReference, @@ -58,11 +149,11 @@ export class BaseRollupHints { public constants: ConstantRollupData, ) {} - static from(fields: FieldsOf): BaseRollupHints { - return new BaseRollupHints(...BaseRollupHints.getFields(fields)); + static from(fields: FieldsOf): BaseRollupHints { + return new PublicBaseRollupHints(...PublicBaseRollupHints.getFields(fields)); } - static getFields(fields: FieldsOf) { + static getFields(fields: FieldsOf) { return [ fields.start, fields.stateDiffHints, @@ -81,7 +172,7 @@ export class BaseRollupHints { * @returns The inputs serialized to a buffer. */ toBuffer() { - return serializeToBuffer(...BaseRollupHints.getFields(this)); + return serializeToBuffer(...PublicBaseRollupHints.getFields(this)); } /** @@ -94,7 +185,7 @@ export class BaseRollupHints { static fromBuffer(buffer: Buffer | BufferReader): BaseRollupHints { const reader = BufferReader.asReader(buffer); - return new BaseRollupHints( + return new PublicBaseRollupHints( reader.readObject(PartialStateReference), reader.readObject(StateDiffHints), reader.readObject(PublicDataHint), @@ -110,11 +201,11 @@ export class BaseRollupHints { } static fromString(str: string) { - return BaseRollupHints.fromBuffer(Buffer.from(str, 'hex')); + return PublicBaseRollupHints.fromBuffer(Buffer.from(str, 'hex')); } static empty() { - return new BaseRollupHints( + return new PublicBaseRollupHints( PartialStateReference.empty(), StateDiffHints.empty(), PublicDataHint.empty(), 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 2ed19ccaf27..f5f04c90b06 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -2630,20 +2630,6 @@ export function mapPrivateBaseRollupInputsToNoir(inputs: PrivateBaseRollupInputs start: mapPartialStateReferenceToNoir(inputs.hints.start), state_diff_hints: mapStateDiffHintsToNoir(inputs.hints.stateDiffHints), - sorted_public_data_writes: mapTuple(inputs.hints.sortedPublicDataWrites, mapPublicDataTreeLeafToNoir), - - sorted_public_data_writes_indexes: mapTuple(inputs.hints.sortedPublicDataWritesIndexes, mapNumberToNoir), - - low_public_data_writes_preimages: mapTuple( - inputs.hints.lowPublicDataWritesPreimages, - mapPublicDataTreePreimageToNoir, - ), - - low_public_data_writes_witnesses: mapTuple( - inputs.hints.lowPublicDataWritesMembershipWitnesses, - (witness: MembershipWitness) => mapMembershipWitnessToNoir(witness), - ), - archive_root_membership_witness: mapMembershipWitnessToNoir(inputs.hints.archiveRootMembershipWitness), constants: mapConstantRollupDataToNoir(inputs.hints.constants), fee_payer_fee_juice_balance_read_hint: mapPublicDataHintToNoir(inputs.hints.feePayerFeeJuiceBalanceReadHint), From 770eb08e208eb9b70681bbe9d9b90e67956dd927 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 14 Nov 2024 13:15:09 +0000 Subject: [PATCH 03/25] wip --- .../rollup-lib/src/base/state_diff_hints.nr | 3 - .../src/structs/rollup/base_rollup_hints.ts | 86 ++-------- .../rollup/private_base_rollup_inputs.ts | 8 +- .../rollup/public_base_rollup_inputs.ts | 12 +- .../structs/rollup/state_diff_hints.test.ts | 18 +- .../src/structs/rollup/state_diff_hints.ts | 158 ++++++++++++++++-- .../circuits.js/src/tests/factories.ts | 126 ++++++++++---- .../src/type_conversion.ts | 57 ++++--- 8 files changed, 307 insertions(+), 161 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr index 2144f1447bb..4b72c37cc3c 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr @@ -39,9 +39,6 @@ pub struct PublicBaseStateDiffHints { // snapshots of the relevant trees are stored in partial state reference). note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], - - sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], public_data_tree_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], diff --git a/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts b/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts index 1eb4cffc331..d9176c82f6d 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_rollup_hints.ts @@ -1,20 +1,12 @@ -import { makeTuple } from '@aztec/foundation/array'; -import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { - ARCHIVE_HEIGHT, - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - PUBLIC_DATA_TREE_HEIGHT, -} from '../../constants.gen.js'; +import { ARCHIVE_HEIGHT } from '../../constants.gen.js'; import { MembershipWitness } from '../membership_witness.js'; import { PartialStateReference } from '../partial_state_reference.js'; import { PublicDataHint } from '../public_data_hint.js'; -import { type UInt32 } from '../shared.js'; -import { PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '../trees/index.js'; import { ConstantRollupData } from './constant_rollup_data.js'; -import { StateDiffHints } from './state_diff_hints.js'; +import { PrivateBaseStateDiffHints, PublicBaseStateDiffHints } from './state_diff_hints.js'; export type BaseRollupHints = PrivateBaseRollupHints | PublicBaseRollupHints; @@ -23,14 +15,10 @@ export class PrivateBaseRollupHints { /** Partial state reference at the start of the rollup. */ public start: PartialStateReference, /** Hints used while proving state diff validity. */ - public stateDiffHints: StateDiffHints, + public stateDiffHints: PrivateBaseStateDiffHints, /** Public data read hint for accessing the balance of the fee payer. */ public feePayerFeeJuiceBalanceReadHint: PublicDataHint, - public feeWriteLowLeafPreimage: PublicDataTreeLeafPreimage, - public feeWriteLowLeafMembershipWitness: MembershipWitness, - public feeWriteSiblingPath: Tuple, - /** * Membership witnesses of blocks referred by each of the 2 kernels. */ @@ -41,7 +29,7 @@ export class PrivateBaseRollupHints { public constants: ConstantRollupData, ) {} - static from(fields: FieldsOf): BaseRollupHints { + static from(fields: FieldsOf): PrivateBaseRollupHints { return new PrivateBaseRollupHints(...PrivateBaseRollupHints.getFields(fields)); } @@ -50,9 +38,6 @@ export class PrivateBaseRollupHints { fields.start, fields.stateDiffHints, fields.feePayerFeeJuiceBalanceReadHint, - fields.feeWriteLowLeafPreimage, - fields.feeWriteLowLeafMembershipWitness, - fields.feeWriteSiblingPath, fields.archiveRootMembershipWitness, fields.constants, ] as const; @@ -74,15 +59,12 @@ export class PrivateBaseRollupHints { return this.toBuffer().toString('hex'); } - static fromBuffer(buffer: Buffer | BufferReader): BaseRollupHints { + static fromBuffer(buffer: Buffer | BufferReader): PrivateBaseRollupHints { const reader = BufferReader.asReader(buffer); return new PrivateBaseRollupHints( reader.readObject(PartialStateReference), - reader.readObject(StateDiffHints), + reader.readObject(PrivateBaseStateDiffHints), reader.readObject(PublicDataHint), - reader.readObject(PublicDataTreeLeafPreimage), - MembershipWitness.fromBuffer(reader, PUBLIC_DATA_TREE_HEIGHT), - reader.readArray(PUBLIC_DATA_TREE_HEIGHT, Fr), MembershipWitness.fromBuffer(reader, ARCHIVE_HEIGHT), reader.readObject(ConstantRollupData), ); @@ -95,11 +77,8 @@ export class PrivateBaseRollupHints { static empty() { return new PrivateBaseRollupHints( PartialStateReference.empty(), - StateDiffHints.empty(), + PrivateBaseStateDiffHints.empty(), PublicDataHint.empty(), - PublicDataTreeLeafPreimage.empty(), - MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT), - makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero), MembershipWitness.empty(ARCHIVE_HEIGHT), ConstantRollupData.empty(), ); @@ -111,34 +90,9 @@ export class PublicBaseRollupHints { /** Partial state reference at the start of the rollup. */ public start: PartialStateReference, /** Hints used while proving state diff validity. */ - public stateDiffHints: StateDiffHints, + public stateDiffHints: PublicBaseStateDiffHints, /** Public data read hint for accessing the balance of the fee payer. */ public feePayerFeeJuiceBalanceReadHint: PublicDataHint, - /** - * The public data writes to be inserted in the tree, sorted high slot to low slot. - */ - public sortedPublicDataWrites: Tuple, - - /** - * The indexes of the sorted public data writes to the original ones. - */ - public sortedPublicDataWritesIndexes: Tuple, - /** - * The public data writes which need to be updated to perform the batch insertion of the new public data writes. - * See `StandardIndexedTree.batchInsert` function for more details. - */ - public lowPublicDataWritesPreimages: Tuple< - PublicDataTreeLeafPreimage, - typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - >, - /** - * Membership witnesses for the nullifiers which need to be updated to perform the batch insertion of the new - * nullifiers. - */ - public lowPublicDataWritesMembershipWitnesses: Tuple< - MembershipWitness, - typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - >, /** * Membership witnesses of blocks referred by each of the 2 kernels. */ @@ -149,7 +103,7 @@ export class PublicBaseRollupHints { public constants: ConstantRollupData, ) {} - static from(fields: FieldsOf): BaseRollupHints { + static from(fields: FieldsOf): PublicBaseRollupHints { return new PublicBaseRollupHints(...PublicBaseRollupHints.getFields(fields)); } @@ -158,10 +112,6 @@ export class PublicBaseRollupHints { fields.start, fields.stateDiffHints, fields.feePayerFeeJuiceBalanceReadHint, - fields.sortedPublicDataWrites, - fields.sortedPublicDataWritesIndexes, - fields.lowPublicDataWritesPreimages, - fields.lowPublicDataWritesMembershipWitnesses, fields.archiveRootMembershipWitness, fields.constants, ] as const; @@ -183,18 +133,12 @@ export class PublicBaseRollupHints { return this.toBuffer().toString('hex'); } - static fromBuffer(buffer: Buffer | BufferReader): BaseRollupHints { + static fromBuffer(buffer: Buffer | BufferReader): PublicBaseRollupHints { const reader = BufferReader.asReader(buffer); return new PublicBaseRollupHints( reader.readObject(PartialStateReference), - reader.readObject(StateDiffHints), + reader.readObject(PublicBaseStateDiffHints), reader.readObject(PublicDataHint), - reader.readArray(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataTreeLeaf), - reader.readNumbers(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX), - reader.readArray(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataTreeLeafPreimage), - reader.readArray(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, { - fromBuffer: buffer => MembershipWitness.fromBuffer(buffer, PUBLIC_DATA_TREE_HEIGHT), - }), MembershipWitness.fromBuffer(reader, ARCHIVE_HEIGHT), reader.readObject(ConstantRollupData), ); @@ -207,12 +151,8 @@ export class PublicBaseRollupHints { static empty() { return new PublicBaseRollupHints( PartialStateReference.empty(), - StateDiffHints.empty(), + PublicBaseStateDiffHints.empty(), PublicDataHint.empty(), - makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataTreeLeaf.empty), - makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => 0), - makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataTreeLeafPreimage.empty), - makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT)), MembershipWitness.empty(ARCHIVE_HEIGHT), ConstantRollupData.empty(), ); diff --git a/yarn-project/circuits.js/src/structs/rollup/private_base_rollup_inputs.ts b/yarn-project/circuits.js/src/structs/rollup/private_base_rollup_inputs.ts index 5cc93fdeb4e..1d354861e1a 100644 --- a/yarn-project/circuits.js/src/structs/rollup/private_base_rollup_inputs.ts +++ b/yarn-project/circuits.js/src/structs/rollup/private_base_rollup_inputs.ts @@ -2,11 +2,11 @@ import { hexSchemaFor } from '@aztec/foundation/schemas'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { BaseRollupHints } from './base_rollup_hints.js'; +import { PrivateBaseRollupHints } from './base_rollup_hints.js'; import { PrivateTubeData } from './private_tube_data.js'; export class PrivateBaseRollupInputs { - constructor(public tubeData: PrivateTubeData, public hints: BaseRollupHints) {} + constructor(public tubeData: PrivateTubeData, public hints: PrivateBaseRollupHints) {} static from(fields: FieldsOf): PrivateBaseRollupInputs { return new PrivateBaseRollupInputs(...PrivateBaseRollupInputs.getFields(fields)); @@ -18,7 +18,7 @@ export class PrivateBaseRollupInputs { static fromBuffer(buffer: Buffer | BufferReader): PrivateBaseRollupInputs { const reader = BufferReader.asReader(buffer); - return new PrivateBaseRollupInputs(reader.readObject(PrivateTubeData), reader.readObject(BaseRollupHints)); + return new PrivateBaseRollupInputs(reader.readObject(PrivateTubeData), reader.readObject(PrivateBaseRollupHints)); } toBuffer() { @@ -34,7 +34,7 @@ export class PrivateBaseRollupInputs { } static empty() { - return new PrivateBaseRollupInputs(PrivateTubeData.empty(), BaseRollupHints.empty()); + return new PrivateBaseRollupInputs(PrivateTubeData.empty(), PrivateBaseRollupHints.empty()); } /** Returns a hex representation for JSON serialization. */ diff --git a/yarn-project/circuits.js/src/structs/rollup/public_base_rollup_inputs.ts b/yarn-project/circuits.js/src/structs/rollup/public_base_rollup_inputs.ts index b8ab34a167f..1fa11ed3688 100644 --- a/yarn-project/circuits.js/src/structs/rollup/public_base_rollup_inputs.ts +++ b/yarn-project/circuits.js/src/structs/rollup/public_base_rollup_inputs.ts @@ -3,11 +3,15 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; import { AvmProofData } from './avm_proof_data.js'; -import { BaseRollupHints } from './base_rollup_hints.js'; +import { PublicBaseRollupHints } from './base_rollup_hints.js'; import { PublicTubeData } from './public_tube_data.js'; export class PublicBaseRollupInputs { - constructor(public tubeData: PublicTubeData, public avmProofData: AvmProofData, public hints: BaseRollupHints) {} + constructor( + public tubeData: PublicTubeData, + public avmProofData: AvmProofData, + public hints: PublicBaseRollupHints, + ) {} static from(fields: FieldsOf): PublicBaseRollupInputs { return new PublicBaseRollupInputs(...PublicBaseRollupInputs.getFields(fields)); @@ -22,7 +26,7 @@ export class PublicBaseRollupInputs { return new PublicBaseRollupInputs( reader.readObject(PublicTubeData), reader.readObject(AvmProofData), - reader.readObject(BaseRollupHints), + reader.readObject(PublicBaseRollupHints), ); } @@ -38,7 +42,7 @@ export class PublicBaseRollupInputs { } static empty() { - return new PublicBaseRollupInputs(PublicTubeData.empty(), AvmProofData.empty(), BaseRollupHints.empty()); + return new PublicBaseRollupInputs(PublicTubeData.empty(), AvmProofData.empty(), PublicBaseRollupHints.empty()); } /** Returns a hex representation for JSON serialization. */ diff --git a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts index b3c9a9f0ac6..c5a6d3ad325 100644 --- a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts +++ b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts @@ -1,11 +1,19 @@ -import { makeStateDiffHints } from '../../tests/factories.js'; -import { StateDiffHints } from './state_diff_hints.js'; +import { makePrivateBaseStateDiffHints, makePublicBaseStateDiffHints } from '../../tests/factories.js'; +import { PrivateBaseStateDiffHints, PublicBaseStateDiffHints } from './state_diff_hints.js'; describe('StateDiffHints', () => { - it('serializes to buffer and deserializes it back', () => { - const expected = makeStateDiffHints(); + it('serializes private hints to buffer and deserializes it back', () => { + const expected = makePrivateBaseStateDiffHints(); const buffer = expected.toBuffer(); - const res = StateDiffHints.fromBuffer(buffer); + const res = PrivateBaseStateDiffHints.fromBuffer(buffer); + expect(res).toEqual(expected); + }); + + + it('serializes public hints to buffer and deserializes it back', () => { + const expected = makePublicBaseStateDiffHints(); + const buffer = expected.toBuffer(); + const res = PublicBaseStateDiffHints.fromBuffer(buffer); expect(res).toEqual(expected); }); }); diff --git a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts index 288f5c5d85b..a59b8d7466f 100644 --- a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts +++ b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts @@ -5,18 +5,19 @@ import { type FieldsOf } from '@aztec/foundation/types'; import { MAX_NULLIFIERS_PER_TX, + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, + PUBLIC_DATA_TREE_HEIGHT, } from '../../constants.gen.js'; import { MembershipWitness } from '../membership_witness.js'; -import { NullifierLeafPreimage } from '../trees/index.js'; +import { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '../trees/index.js'; /** - * Hints used while proving state diff validity. + * Hints used while proving state diff validity for the private base rollup. */ -export class StateDiffHints { +export class PrivateBaseStateDiffHints { constructor( /** * The nullifiers which need to be updated to perform the batch insertion of the new nullifiers. @@ -47,17 +48,128 @@ export class StateDiffHints { * Sibling path "pointing to" where the new nullifiers subtree should be inserted into the nullifier tree. */ public nullifierSubtreeSiblingPath: Tuple, + + public feeWriteLowLeafPreimage: PublicDataTreeLeafPreimage, + public feeWriteLowLeafMembershipWitness: MembershipWitness, + public feeWriteSiblingPath: Tuple, + ) {} + + static from(fields: FieldsOf): PrivateBaseStateDiffHints { + return new PrivateBaseStateDiffHints(...PrivateBaseStateDiffHints.getFields(fields)); + } + + static getFields(fields: FieldsOf) { + return [ + fields.nullifierPredecessorPreimages, + fields.nullifierPredecessorMembershipWitnesses, + fields.sortedNullifiers, + fields.sortedNullifierIndexes, + fields.noteHashSubtreeSiblingPath, + fields.nullifierSubtreeSiblingPath, + fields.feeWriteLowLeafPreimage, + fields.feeWriteLowLeafMembershipWitness, + fields.feeWriteSiblingPath, + ] as const; + } + + /** + * Serializes the state diff hints to a buffer. + * @returns A buffer of the serialized state diff hints. + */ + toBuffer(): Buffer { + return serializeToBuffer(...PrivateBaseStateDiffHints.getFields(this)); + } + + /** + * Deserializes the state diff hints from a buffer. + * @param buffer - A buffer to deserialize from. + * @returns A new PrivateBaseStateDiffHints instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PrivateBaseStateDiffHints { + const reader = BufferReader.asReader(buffer); + return new PrivateBaseStateDiffHints( + reader.readArray(MAX_NULLIFIERS_PER_TX, NullifierLeafPreimage), + reader.readArray(MAX_NULLIFIERS_PER_TX, { + fromBuffer: buffer => MembershipWitness.fromBuffer(buffer, NULLIFIER_TREE_HEIGHT), + }), + reader.readArray(MAX_NULLIFIERS_PER_TX, Fr), + reader.readNumbers(MAX_NULLIFIERS_PER_TX), + reader.readArray(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, Fr), + reader.readArray(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, Fr), + reader.readObject(PublicDataTreeLeafPreimage), + MembershipWitness.fromBuffer(reader, PUBLIC_DATA_TREE_HEIGHT), + reader.readArray(PUBLIC_DATA_TREE_HEIGHT, Fr), + ); + } + + static empty() { + return new PrivateBaseStateDiffHints( + makeTuple(MAX_NULLIFIERS_PER_TX, NullifierLeafPreimage.empty), + makeTuple(MAX_NULLIFIERS_PER_TX, () => MembershipWitness.empty(NULLIFIER_TREE_HEIGHT)), + makeTuple(MAX_NULLIFIERS_PER_TX, Fr.zero), + makeTuple(MAX_NULLIFIERS_PER_TX, () => 0), + makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, Fr.zero), + makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, Fr.zero), + PublicDataTreeLeafPreimage.empty(), + MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT), + makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero), + ); + } +} + +export class PublicBaseStateDiffHints { + constructor( + /** + * The nullifiers which need to be updated to perform the batch insertion of the new nullifiers. + * See `StandardIndexedTree.batchInsert` function for more details. + */ + public nullifierPredecessorPreimages: Tuple, + /** + * Membership witnesses for the nullifiers which need to be updated to perform the batch insertion of the new + * nullifiers. + */ + public nullifierPredecessorMembershipWitnesses: Tuple< + MembershipWitness, + typeof MAX_NULLIFIERS_PER_TX + >, + /** + * The nullifiers to be inserted in the tree, sorted high to low. + */ + public sortedNullifiers: Tuple, /** - * Sibling path "pointing to" where the new public data subtree should be inserted into the public data tree. + * The indexes of the sorted nullifiers to the original ones. */ - public publicDataSiblingPath: Tuple, + public sortedNullifierIndexes: Tuple, + /** + * Sibling path "pointing to" where the new note hash subtree should be inserted into the note hash tree. + */ + public noteHashSubtreeSiblingPath: Tuple, + /** + * Sibling path "pointing to" where the new nullifiers subtree should be inserted into the nullifier tree. + */ + public nullifierSubtreeSiblingPath: Tuple, + + public lowPublicDataWritesPreimages: Tuple< + PublicDataTreeLeafPreimage, + typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + >, + + public lowPublicDataWritesMembershipWitnesses: Tuple< + MembershipWitness, + typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + >, + + public publicDataTreeSiblingPaths: Tuple< + Tuple, + typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + >, ) {} - static from(fields: FieldsOf): StateDiffHints { - return new StateDiffHints(...StateDiffHints.getFields(fields)); + static from(fields: FieldsOf): PublicBaseStateDiffHints { + return new PublicBaseStateDiffHints(...PublicBaseStateDiffHints.getFields(fields)); } - static getFields(fields: FieldsOf) { + static getFields(fields: FieldsOf) { return [ fields.nullifierPredecessorPreimages, fields.nullifierPredecessorMembershipWitnesses, @@ -65,7 +177,9 @@ export class StateDiffHints { fields.sortedNullifierIndexes, fields.noteHashSubtreeSiblingPath, fields.nullifierSubtreeSiblingPath, - fields.publicDataSiblingPath, + fields.lowPublicDataWritesPreimages, + fields.lowPublicDataWritesMembershipWitnesses, + fields.publicDataTreeSiblingPaths, ] as const; } @@ -74,17 +188,17 @@ export class StateDiffHints { * @returns A buffer of the serialized state diff hints. */ toBuffer(): Buffer { - return serializeToBuffer(...StateDiffHints.getFields(this)); + return serializeToBuffer(...PublicBaseStateDiffHints.getFields(this)); } /** * Deserializes the state diff hints from a buffer. * @param buffer - A buffer to deserialize from. - * @returns A new StateDiffHints instance. + * @returns A new PublicBaseStateDiffHints instance. */ - static fromBuffer(buffer: Buffer | BufferReader): StateDiffHints { + static fromBuffer(buffer: Buffer | BufferReader): PublicBaseStateDiffHints { const reader = BufferReader.asReader(buffer); - return new StateDiffHints( + return new PublicBaseStateDiffHints( reader.readArray(MAX_NULLIFIERS_PER_TX, NullifierLeafPreimage), reader.readArray(MAX_NULLIFIERS_PER_TX, { fromBuffer: buffer => MembershipWitness.fromBuffer(buffer, NULLIFIER_TREE_HEIGHT), @@ -93,19 +207,29 @@ export class StateDiffHints { reader.readNumbers(MAX_NULLIFIERS_PER_TX), reader.readArray(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, Fr), reader.readArray(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, Fr), - reader.readArray(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, Fr), + reader.readArray(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataTreeLeafPreimage), + reader.readArray(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, { + fromBuffer: buffer => MembershipWitness.fromBuffer(buffer, PUBLIC_DATA_TREE_HEIGHT), + }), + reader.readArray(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, { + fromBuffer(reader) { + return BufferReader.asReader(reader).readArray(PUBLIC_DATA_TREE_HEIGHT, Fr); + }, + }), ); } static empty() { - return new StateDiffHints( + return new PublicBaseStateDiffHints( makeTuple(MAX_NULLIFIERS_PER_TX, NullifierLeafPreimage.empty), makeTuple(MAX_NULLIFIERS_PER_TX, () => MembershipWitness.empty(NULLIFIER_TREE_HEIGHT)), makeTuple(MAX_NULLIFIERS_PER_TX, Fr.zero), makeTuple(MAX_NULLIFIERS_PER_TX, () => 0), makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, Fr.zero), makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, Fr.zero), - makeTuple(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, Fr.zero), + makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataTreeLeafPreimage.empty), + makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT)), + makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => makeTuple(PUBLIC_DATA_TREE_HEIGHT, Fr.zero)), ); } } diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 8c60ede4f03..11de7f369e4 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -93,7 +93,6 @@ import { NullifierLeafPreimage, NullifierNonExistentReadRequestHintsBuilder, NullifierReadRequestHintsBuilder, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, ParityPublicInputs, PartialPrivateTailPublicInputsForPublic, @@ -128,7 +127,6 @@ import { RootRollupPublicInputs, ScopedLogHash, ScopedReadRequest, - StateDiffHints, StateReference, TUBE_PROOF_LENGTH, TxContext, @@ -160,17 +158,20 @@ import { AvmProofData, AvmPublicDataReadTreeHint, AvmPublicDataWriteTreeHint, - BaseRollupHints, CountedPublicCallRequest, EnqueuedCallData, + PrivateBaseRollupHints, PrivateBaseRollupInputs, + PrivateBaseStateDiffHints, PrivateToAvmAccumulatedData, PrivateToAvmAccumulatedDataArrayLengths, PrivateToPublicAccumulatedData, PrivateToPublicKernelCircuitPublicInputs, PrivateTubeData, PublicAccumulatedDataArrayLengths, + PublicBaseRollupHints, PublicBaseRollupInputs, + PublicBaseStateDiffHints, PublicDataLeafHint, PublicDataWrite, PublicInnerCallRequest, @@ -1257,11 +1258,11 @@ export function makePublicDataTreeLeafPreimage(seed = 0): PublicDataTreeLeafPrei } /** - * Creates an instance of StateDiffHints with arbitrary values based on the provided seed. + * Creates an instance of PrivateBaseStateDiffHints with arbitrary values based on the provided seed. * @param seed - The seed to use for generating the hints. - * @returns A StateDiffHints object. + * @returns A PrivateBaseStateDiffHints object. */ -export function makeStateDiffHints(seed = 1): StateDiffHints { +export function makePrivateBaseStateDiffHints(seed = 1): PrivateBaseStateDiffHints { const nullifierPredecessorPreimages = makeTuple( MAX_NULLIFIERS_PER_TX, x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), @@ -1282,16 +1283,77 @@ export function makeStateDiffHints(seed = 1): StateDiffHints { const nullifierSubtreeSiblingPath = makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x6000); - const publicDataSiblingPath = makeTuple(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, fr, 0x8000); + const feeWriteLowLeafPreimage = makePublicDataTreeLeafPreimage(seed + 0x7000); + const feeWriteLowLeafMembershipWitness = makeMembershipWitness(PUBLIC_DATA_TREE_HEIGHT, seed + 0x8000); + const feeWriteSiblingPath = makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, seed + 0x9000); - return new StateDiffHints( + return new PrivateBaseStateDiffHints( nullifierPredecessorPreimages, nullifierPredecessorMembershipWitnesses, sortedNullifiers, sortedNullifierIndexes, noteHashSubtreeSiblingPath, nullifierSubtreeSiblingPath, - publicDataSiblingPath, + feeWriteLowLeafPreimage, + feeWriteLowLeafMembershipWitness, + feeWriteSiblingPath, + ); +} + +/** + * Creates an instance of PublicBaseStateDiffHints with arbitrary values based on the provided seed. + * @param seed - The seed to use for generating the hints. + * @returns A PublicBaseStateDiffHints object. + */ +export function makePublicBaseStateDiffHints(seed = 1): PublicBaseStateDiffHints { + const nullifierPredecessorPreimages = makeTuple( + MAX_NULLIFIERS_PER_TX, + x => new NullifierLeafPreimage(fr(x), fr(x + 0x100), BigInt(x + 0x200)), + seed + 0x1000, + ); + + const nullifierPredecessorMembershipWitnesses = makeTuple( + MAX_NULLIFIERS_PER_TX, + x => makeMembershipWitness(NULLIFIER_TREE_HEIGHT, x), + seed + 0x2000, + ); + + const sortedNullifiers = makeTuple(MAX_NULLIFIERS_PER_TX, fr, seed + 0x3000); + + const sortedNullifierIndexes = makeTuple(MAX_NULLIFIERS_PER_TX, i => i, seed + 0x4000); + + const noteHashSubtreeSiblingPath = makeTuple(NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x5000); + + const nullifierSubtreeSiblingPath = makeTuple(NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, fr, seed + 0x6000); + + const lowPublicDataWritesPreimages = makeTuple( + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + makePublicDataTreeLeafPreimage, + seed + 0x7000, + ); + + const lowPublicDataWritesMembershipWitnesses = makeTuple( + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + i => makeMembershipWitness(PUBLIC_DATA_TREE_HEIGHT, i), + seed + 0x8000, + ); + + const publicDataTreeSiblingPaths = makeTuple( + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + i => makeTuple(PUBLIC_DATA_TREE_HEIGHT, fr, i), + seed + 0x9000, + ); + + return new PublicBaseStateDiffHints( + nullifierPredecessorPreimages, + nullifierPredecessorMembershipWitnesses, + sortedNullifiers, + sortedNullifierIndexes, + noteHashSubtreeSiblingPath, + nullifierSubtreeSiblingPath, + lowPublicDataWritesPreimages, + lowPublicDataWritesMembershipWitnesses, + publicDataTreeSiblingPaths, ); } @@ -1307,30 +1369,30 @@ function makePrivateTubeData(seed = 1, kernelPublicInputs?: KernelCircuitPublicI ); } -function makeBaseRollupHints(seed = 1) { +function makePrivateBaseRollupHints(seed = 1) { const start = makePartialStateReference(seed + 0x100); - const stateDiffHints = makeStateDiffHints(seed + 0x600); + const stateDiffHints = makePrivateBaseStateDiffHints(seed + 0x600); - const sortedPublicDataWrites = makeTuple( - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - makePublicDataTreeLeaf, - seed + 0x8000, - ); + const archiveRootMembershipWitness = makeMembershipWitness(ARCHIVE_HEIGHT, seed + 0x9000); - const sortedPublicDataWritesIndexes = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => i, 0); + const constants = makeConstantBaseRollupData(0x100); - const lowPublicDataWritesPreimages = makeTuple( - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - makePublicDataTreeLeafPreimage, - seed + 0x8200, - ); + const feePayerFeeJuiceBalanceReadHint = PublicDataHint.empty(); - const lowPublicDataWritesMembershipWitnesses = makeTuple( - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - i => makeMembershipWitness(PUBLIC_DATA_TREE_HEIGHT, i), - seed + 0x8400, - ); + return PrivateBaseRollupHints.from({ + start, + stateDiffHints, + archiveRootMembershipWitness, + constants, + feePayerFeeJuiceBalanceReadHint, + }); +} + +function makePublicBaseRollupHints(seed = 1) { + const start = makePartialStateReference(seed + 0x100); + + const stateDiffHints = makePublicBaseStateDiffHints(seed + 0x600); const archiveRootMembershipWitness = makeMembershipWitness(ARCHIVE_HEIGHT, seed + 0x9000); @@ -1338,13 +1400,9 @@ function makeBaseRollupHints(seed = 1) { const feePayerFeeJuiceBalanceReadHint = PublicDataHint.empty(); - return BaseRollupHints.from({ + return PublicBaseRollupHints.from({ start, stateDiffHints, - sortedPublicDataWrites, - sortedPublicDataWritesIndexes, - lowPublicDataWritesPreimages, - lowPublicDataWritesMembershipWitnesses, archiveRootMembershipWitness, constants, feePayerFeeJuiceBalanceReadHint, @@ -1353,7 +1411,7 @@ function makeBaseRollupHints(seed = 1) { export function makePrivateBaseRollupInputs(seed = 0) { const tubeData = makePrivateTubeData(seed); - const hints = makeBaseRollupHints(seed + 0x100); + const hints = makePrivateBaseRollupHints(seed + 0x100); return PrivateBaseRollupInputs.from({ tubeData, @@ -1380,7 +1438,7 @@ function makeAvmProofData(seed = 1) { export function makePublicBaseRollupInputs(seed = 0) { const tubeData = makePublicTubeData(seed); const avmProofData = makeAvmProofData(seed + 0x100); - const hints = makeBaseRollupHints(seed + 0x100); + const hints = makePublicBaseRollupHints(seed + 0x200); return PublicBaseRollupInputs.from({ tubeData, 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 f5f04c90b06..c7621962cfd 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 { type PreviousRollupData, PrivateAccumulatedData, type PrivateBaseRollupInputs, + type PrivateBaseStateDiffHints, type PrivateCallData, PrivateCallRequest, type PrivateCircuitPublicInputs, @@ -101,6 +102,7 @@ import { PublicAccumulatedData, PublicAccumulatedDataArrayLengths, type PublicBaseRollupInputs, + type PublicBaseStateDiffHints, PublicCallRequest, PublicCallStackItemCompressed, type PublicDataHint, @@ -137,7 +139,6 @@ import { ScopedNullifier, ScopedReadRequest, type SettledReadHint, - type StateDiffHints, StateReference, type TUBE_PROOF_LENGTH, type TransientDataIndexHint, @@ -218,6 +219,7 @@ import type { PreviousRollupData as PreviousRollupDataNoir, PrivateAccumulatedData as PrivateAccumulatedDataNoir, PrivateBaseRollupInputs as PrivateBaseRollupInputsNoir, + PrivateBaseStateDiffHints as PrivateBaseStateDiffHintsNoir, PrivateCallDataWithoutPublicInputs as PrivateCallDataWithoutPublicInputsNoir, PrivateCallRequest as PrivateCallRequestNoir, PrivateCircuitPublicInputs as PrivateCircuitPublicInputsNoir, @@ -234,6 +236,7 @@ import type { PublicAccumulatedDataArrayLengths as PublicAccumulatedDataArrayLengthsNoir, PublicAccumulatedData as PublicAccumulatedDataNoir, PublicBaseRollupInputs as PublicBaseRollupInputsNoir, + PublicBaseStateDiffHints as PublicBaseStateDiffHintsNoir, PublicCallRequest as PublicCallRequestNoir, PublicCallStackItemCompressed as PublicCallStackItemCompressedNoir, PublicDataHint as PublicDataHintNoir, @@ -266,7 +269,6 @@ import type { ScopedNoteHash as ScopedNoteHashNoir, ScopedNullifier as ScopedNullifierNoir, ScopedReadRequest as ScopedReadRequestNoir, - StateDiffHints as StateDiffHintsNoir, StateReference as StateReferenceNoir, TransientDataIndexHint as TransientDataIndexHintNoir, TreeLeafReadRequestHint as TreeLeafReadRequestHintNoir, @@ -2568,11 +2570,11 @@ export function mapPartialStateReferenceToNoir( } /** - * Maps state diff hints to a noir state diff hints. + * Maps private base state diff hints to a noir state diff hints. * @param hints - The state diff hints. * @returns The noir state diff hints. */ -export function mapStateDiffHintsToNoir(hints: StateDiffHints): StateDiffHintsNoir { +export function mapPrivateBaseStateDiffHintsToNoir(hints: PrivateBaseStateDiffHints): PrivateBaseStateDiffHintsNoir { return { nullifier_predecessor_preimages: mapTuple(hints.nullifierPredecessorPreimages, mapNullifierLeafPreimageToNoir), nullifier_predecessor_membership_witnesses: mapTuple( @@ -2583,7 +2585,34 @@ export function mapStateDiffHintsToNoir(hints: StateDiffHints): StateDiffHintsNo sorted_nullifier_indexes: mapTuple(hints.sortedNullifierIndexes, (index: number) => mapNumberToNoir(index)), note_hash_subtree_sibling_path: mapTuple(hints.noteHashSubtreeSiblingPath, mapFieldToNoir), nullifier_subtree_sibling_path: mapTuple(hints.nullifierSubtreeSiblingPath, mapFieldToNoir), - public_data_sibling_path: mapTuple(hints.publicDataSiblingPath, mapFieldToNoir), + fee_write_low_leaf_preimage: mapPublicDataTreePreimageToNoir(hints.feeWriteLowLeafPreimage), + fee_write_low_leaf_membership_witness: mapMembershipWitnessToNoir(hints.feeWriteLowLeafMembershipWitness), + fee_write_sibling_path: mapTuple(hints.feeWriteSiblingPath, mapFieldToNoir), + }; +} + +/** + * Maps public base state diff hints to a noir state diff hints. + * @param hints - The state diff hints. + * @returns The noir state diff hints. + */ +export function mapPublicBaseStateDiffHintsToNoir(hints: PublicBaseStateDiffHints): PublicBaseStateDiffHintsNoir { + return { + nullifier_predecessor_preimages: mapTuple(hints.nullifierPredecessorPreimages, mapNullifierLeafPreimageToNoir), + nullifier_predecessor_membership_witnesses: mapTuple( + hints.nullifierPredecessorMembershipWitnesses, + (witness: MembershipWitness) => mapMembershipWitnessToNoir(witness), + ), + sorted_nullifiers: mapTuple(hints.sortedNullifiers, mapFieldToNoir), + sorted_nullifier_indexes: mapTuple(hints.sortedNullifierIndexes, (index: number) => mapNumberToNoir(index)), + note_hash_subtree_sibling_path: mapTuple(hints.noteHashSubtreeSiblingPath, mapFieldToNoir), + nullifier_subtree_sibling_path: mapTuple(hints.nullifierSubtreeSiblingPath, mapFieldToNoir), + low_public_data_writes_preimages: mapTuple(hints.lowPublicDataWritesPreimages, mapPublicDataTreePreimageToNoir), + low_public_data_writes_witnesses: mapTuple( + hints.lowPublicDataWritesMembershipWitnesses, + (witness: MembershipWitness) => mapMembershipWitnessToNoir(witness), + ), + public_data_tree_sibling_paths: mapTuple(hints.publicDataTreeSiblingPaths, path => mapTuple(path, mapFieldToNoir)), }; } @@ -2628,7 +2657,7 @@ export function mapPrivateBaseRollupInputsToNoir(inputs: PrivateBaseRollupInputs tube_data: mapPrivateTubeDataToNoir(inputs.tubeData), start: mapPartialStateReferenceToNoir(inputs.hints.start), - state_diff_hints: mapStateDiffHintsToNoir(inputs.hints.stateDiffHints), + state_diff_hints: mapPrivateBaseStateDiffHintsToNoir(inputs.hints.stateDiffHints), archive_root_membership_witness: mapMembershipWitnessToNoir(inputs.hints.archiveRootMembershipWitness), constants: mapConstantRollupDataToNoir(inputs.hints.constants), @@ -2658,21 +2687,7 @@ export function mapPublicBaseRollupInputsToNoir(inputs: PublicBaseRollupInputs): avm_proof_data: mapAvmProofDataToNoir(inputs.avmProofData), start: mapPartialStateReferenceToNoir(inputs.hints.start), - state_diff_hints: mapStateDiffHintsToNoir(inputs.hints.stateDiffHints), - - sorted_public_data_writes: mapTuple(inputs.hints.sortedPublicDataWrites, mapPublicDataTreeLeafToNoir), - - sorted_public_data_writes_indexes: mapTuple(inputs.hints.sortedPublicDataWritesIndexes, mapNumberToNoir), - - low_public_data_writes_preimages: mapTuple( - inputs.hints.lowPublicDataWritesPreimages, - mapPublicDataTreePreimageToNoir, - ), - - low_public_data_writes_witnesses: mapTuple( - inputs.hints.lowPublicDataWritesMembershipWitnesses, - (witness: MembershipWitness) => mapMembershipWitnessToNoir(witness), - ), + state_diff_hints: mapPublicBaseStateDiffHintsToNoir(inputs.hints.stateDiffHints), archive_root_membership_witness: mapMembershipWitnessToNoir(inputs.hints.archiveRootMembershipWitness), constants: mapConstantRollupDataToNoir(inputs.hints.constants), From 34d630a3e299ce3828d6a0f7d14cc6d4ff8d4323 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 15 Nov 2024 11:02:23 +0000 Subject: [PATCH 04/25] fixes --- .../cached_content_addressed_tree_store.hpp | 44 ++-- .../rollup-lib/src/base/state_diff_hints.nr | 1 + .../src/type_conversion.ts | 12 - .../orchestrator/block-building-helpers.ts | 234 +++++++++++------- .../src/orchestrator/tx-proving-state.ts | 9 + .../src/test/bb_prover_base_rollup.test.ts | 3 +- .../src/block_builder/light.test.ts | 3 +- .../src/native/native_world_state.ts | 17 +- 8 files changed, 188 insertions(+), 135 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp index 9b113365e1a..bffe4881d49 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp @@ -12,6 +12,7 @@ #include "msgpack/assert.hpp" #include #include +#include #include #include #include @@ -637,19 +638,15 @@ template void ContentAddressedCachedTreeStore void ContentAddressedCachedTreeStore(uncommittedMeta.root), 0, *tx); - if (asBlock) { - ++uncommittedMeta.unfinalisedBlockHeight; - if (uncommittedMeta.oldestHistoricBlock == 0) { - uncommittedMeta.oldestHistoricBlock = 1; - } - // std::cout << "New root " << uncommittedMeta.root << std::endl; - BlockPayload block{ .size = uncommittedMeta.size, - .blockNumber = uncommittedMeta.unfinalisedBlockHeight, - .root = uncommittedMeta.root }; - dataStore_->write_block_data(uncommittedMeta.unfinalisedBlockHeight, block, *tx); + std::cout << "Persisted root" << std::endl; + } + if (asBlock) { + ++uncommittedMeta.unfinalisedBlockHeight; + if (uncommittedMeta.oldestHistoricBlock == 0) { + uncommittedMeta.oldestHistoricBlock = 1; } + // std::cout << "New root " << uncommittedMeta.root << std::endl; + BlockPayload block{ .size = uncommittedMeta.size, + .blockNumber = uncommittedMeta.unfinalisedBlockHeight, + .root = uncommittedMeta.root }; + dataStore_->write_block_data(uncommittedMeta.unfinalisedBlockHeight, block, *tx); } + uncommittedMeta.committedSize = uncommittedMeta.size; persist_meta(uncommittedMeta, *tx); tx->commit(); @@ -1129,6 +1130,9 @@ void ContentAddressedCachedTreeStore::initialise_from_block(const } if (meta_.unfinalisedBlockHeight < blockNumber) { + std::cout << "Unfinalised block height " << meta_.unfinalisedBlockHeight << std::endl; + std::cout << "Block number " << blockNumber << std::endl; + std::cout << "Meta " << meta_ << std::endl; throw std::runtime_error("Unable to initialise from future block"); } if (meta_.oldestHistoricBlock > blockNumber && blockNumber != 0) { diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr index 4b72c37cc3c..c9440abad08 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr @@ -39,6 +39,7 @@ pub struct PublicBaseStateDiffHints { // snapshots of the relevant trees are stored in partial state reference). note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], public_data_tree_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], 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 c7621962cfd..5ee80a1ec59 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -108,7 +108,6 @@ import { type PublicDataHint, type PublicDataLeafHint, PublicDataRead, - type PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, PublicDataUpdateRequest, PublicDataWrite, @@ -242,7 +241,6 @@ import type { PublicDataHint as PublicDataHintNoir, PublicDataLeafHint as PublicDataLeafHintNoir, PublicDataRead as PublicDataReadNoir, - PublicDataTreeLeaf as PublicDataTreeLeafNoir, PublicDataTreeLeafPreimage as PublicDataTreeLeafPreimageNoir, PublicDataUpdateRequest as PublicDataUpdateRequestNoir, PublicDataWrite as PublicDataWriteNoir, @@ -2532,16 +2530,6 @@ function mapMembershipWitnessToNoir(witness: MembershipWitness }; } -/** - * Maps a leaf of the public data tree to noir. - */ -export function mapPublicDataTreeLeafToNoir(leaf: PublicDataTreeLeaf): PublicDataTreeLeafNoir { - return { - slot: mapFieldToNoir(leaf.slot), - value: mapFieldToNoir(leaf.value), - }; -} - /** * Maps a leaf preimage of the public data tree to noir. */ diff --git a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts index 75044301ba5..7e4a1049b23 100644 --- a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts +++ b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts @@ -10,7 +10,6 @@ import { ARCHIVE_HEIGHT, AppendOnlyTreeSnapshot, type BaseOrMergeRollupPublicInputs, - BaseRollupHints, BlockMergeRollupInputs, type BlockRootOrBlockMergePublicInputs, ConstantRollupData, @@ -32,25 +31,25 @@ import { NULLIFIER_TREE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NullifierLeafPreimage, - PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, type ParityPublicInputs, PartialStateReference, PreviousRollupBlockData, PreviousRollupData, + PrivateBaseRollupHints, + PrivateBaseStateDiffHints, + PublicBaseRollupHints, + PublicBaseStateDiffHints, PublicDataHint, PublicDataTreeLeaf, - type PublicDataTreeLeafPreimage, - PublicDataWrite, + PublicDataTreeLeafPreimage, type RecursiveProof, RootRollupInputs, - StateDiffHints, StateReference, VK_TREE_HEIGHT, type VerificationKeyAsFields, } from '@aztec/circuits.js'; -import { assertPermutation, makeTuple } from '@aztec/foundation/array'; +import { makeTuple } from '@aztec/foundation/array'; import { padArrayEnd } from '@aztec/foundation/collection'; import { sha256Trunc } from '@aztec/foundation/crypto'; import { type DebugLogger } from '@aztec/foundation/log'; @@ -140,45 +139,109 @@ export async function buildBaseRollupHints( i < nullifierSubtreeSiblingPathArray.length ? nullifierSubtreeSiblingPathArray[i] : Fr.ZERO, ); - const publicDataSiblingPath = txPublicDataUpdateRequestInfo.newPublicDataSubtreeSiblingPath; - - const stateDiffHints = StateDiffHints.from({ - nullifierPredecessorPreimages: makeTuple(MAX_NULLIFIERS_PER_TX, i => - i < nullifierWitnessLeaves.length - ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage) - : NullifierLeafPreimage.empty(), - ), - nullifierPredecessorMembershipWitnesses: makeTuple(MAX_NULLIFIERS_PER_TX, i => - i < nullifierPredecessorMembershipWitnessesWithoutPadding.length - ? nullifierPredecessorMembershipWitnessesWithoutPadding[i] - : makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT), - ), - sortedNullifiers: makeTuple(MAX_NULLIFIERS_PER_TX, i => Fr.fromBuffer(sortednullifiers[i])), - sortedNullifierIndexes: makeTuple(MAX_NULLIFIERS_PER_TX, i => sortedNewLeavesIndexes[i]), - noteHashSubtreeSiblingPath, - nullifierSubtreeSiblingPath, - publicDataSiblingPath, - }); + if (tx.avmProvingRequest) { + // Build public base rollup hints + const stateDiffHints = PublicBaseStateDiffHints.from({ + nullifierPredecessorPreimages: makeTuple(MAX_NULLIFIERS_PER_TX, i => + i < nullifierWitnessLeaves.length + ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage) + : NullifierLeafPreimage.empty(), + ), + nullifierPredecessorMembershipWitnesses: makeTuple(MAX_NULLIFIERS_PER_TX, i => + i < nullifierPredecessorMembershipWitnessesWithoutPadding.length + ? nullifierPredecessorMembershipWitnessesWithoutPadding[i] + : makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT), + ), + sortedNullifiers: makeTuple(MAX_NULLIFIERS_PER_TX, i => Fr.fromBuffer(sortednullifiers[i])), + sortedNullifierIndexes: makeTuple(MAX_NULLIFIERS_PER_TX, i => sortedNewLeavesIndexes[i]), + noteHashSubtreeSiblingPath, + nullifierSubtreeSiblingPath, + lowPublicDataWritesPreimages: padArrayEnd( + txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages, + PublicDataTreeLeafPreimage.empty(), + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ), + lowPublicDataWritesMembershipWitnesses: padArrayEnd( + txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses, + MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT), + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ), + publicDataTreeSiblingPaths: padArrayEnd( + txPublicDataUpdateRequestInfo.publicDataWritesSiblingPaths, + makeTuple(PUBLIC_DATA_TREE_HEIGHT, () => Fr.ZERO), + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + ), + }); + + const blockHash = tx.constants.historicalHeader.hash(); + const archiveRootMembershipWitness = await getMembershipWitnessFor( + blockHash, + MerkleTreeId.ARCHIVE, + ARCHIVE_HEIGHT, + db, + ); - const blockHash = tx.constants.historicalHeader.hash(); - const archiveRootMembershipWitness = await getMembershipWitnessFor( - blockHash, - MerkleTreeId.ARCHIVE, - ARCHIVE_HEIGHT, - db, - ); + return PublicBaseRollupHints.from({ + start, + stateDiffHints, + feePayerFeeJuiceBalanceReadHint: feePayerFeeJuiceBalanceReadHint, + archiveRootMembershipWitness, + constants, + }); + } else { + if ( + txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses.length > 1 || + txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages.length > 1 || + txPublicDataUpdateRequestInfo.publicDataWritesSiblingPaths.length > 1 + ) { + throw new Error(`More than one public data write in a private only tx`); + } + + const feeWriteLowLeafPreimage = + txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages[0] || PublicDataTreeLeafPreimage.empty(); + const feeWriteLowLeafMembershipWitness = + txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses[0] || + MembershipWitness.empty(PUBLIC_DATA_TREE_HEIGHT); + const feeWriteSiblingPath = + txPublicDataUpdateRequestInfo.publicDataWritesSiblingPaths[0] || + makeTuple(PUBLIC_DATA_TREE_HEIGHT, () => Fr.ZERO); + + const stateDiffHints = PrivateBaseStateDiffHints.from({ + nullifierPredecessorPreimages: makeTuple(MAX_NULLIFIERS_PER_TX, i => + i < nullifierWitnessLeaves.length + ? (nullifierWitnessLeaves[i].leafPreimage as NullifierLeafPreimage) + : NullifierLeafPreimage.empty(), + ), + nullifierPredecessorMembershipWitnesses: makeTuple(MAX_NULLIFIERS_PER_TX, i => + i < nullifierPredecessorMembershipWitnessesWithoutPadding.length + ? nullifierPredecessorMembershipWitnessesWithoutPadding[i] + : makeEmptyMembershipWitness(NULLIFIER_TREE_HEIGHT), + ), + sortedNullifiers: makeTuple(MAX_NULLIFIERS_PER_TX, i => Fr.fromBuffer(sortednullifiers[i])), + sortedNullifierIndexes: makeTuple(MAX_NULLIFIERS_PER_TX, i => sortedNewLeavesIndexes[i]), + noteHashSubtreeSiblingPath, + nullifierSubtreeSiblingPath, + feeWriteLowLeafPreimage, + feeWriteLowLeafMembershipWitness, + feeWriteSiblingPath, + }); + + const blockHash = tx.constants.historicalHeader.hash(); + const archiveRootMembershipWitness = await getMembershipWitnessFor( + blockHash, + MerkleTreeId.ARCHIVE, + ARCHIVE_HEIGHT, + db, + ); - return BaseRollupHints.from({ - start, - stateDiffHints, - feePayerFeeJuiceBalanceReadHint: feePayerFeeJuiceBalanceReadHint, - sortedPublicDataWrites: txPublicDataUpdateRequestInfo.sortedPublicDataWrites, - sortedPublicDataWritesIndexes: txPublicDataUpdateRequestInfo.sortedPublicDataWritesIndexes, - lowPublicDataWritesPreimages: txPublicDataUpdateRequestInfo.lowPublicDataWritesPreimages, - lowPublicDataWritesMembershipWitnesses: txPublicDataUpdateRequestInfo.lowPublicDataWritesMembershipWitnesses, - archiveRootMembershipWitness, - constants, - }); + return PrivateBaseRollupHints.from({ + start, + stateDiffHints, + feePayerFeeJuiceBalanceReadHint: feePayerFeeJuiceBalanceReadHint, + archiveRootMembershipWitness, + constants, + }); + } } export function createMergeRollupInputs( @@ -397,67 +460,50 @@ export function makeEmptyMembershipWitness(height: N) { } async function processPublicDataUpdateRequests(tx: ProcessedTx, db: MerkleTreeWriteOperations) { - const allPublicDataUpdateRequests = padArrayEnd( - tx.txEffect.publicDataWrites, - PublicDataWrite.empty(), - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); - - const allPublicDataWrites = allPublicDataUpdateRequests.map( + const allPublicDataWrites = tx.txEffect.publicDataWrites.map( ({ leafSlot, value }) => new PublicDataTreeLeaf(leafSlot, value), ); - const { lowLeavesWitnessData, newSubtreeSiblingPath, sortedNewLeaves, sortedNewLeavesIndexes } = await db.batchInsert( - MerkleTreeId.PUBLIC_DATA_TREE, - allPublicDataWrites.map(x => x.toBuffer()), - // TODO(#3675) remove oldValue from update requests - PUBLIC_DATA_SUBTREE_HEIGHT, - ); - - if (lowLeavesWitnessData === undefined) { - throw new Error(`Could not craft public data batch insertion proofs`); - } - - const sortedPublicDataWrites = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { - return PublicDataTreeLeaf.fromBuffer(sortedNewLeaves[i]); - }); - - const sortedPublicDataWritesIndexes = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { - return sortedNewLeavesIndexes[i]; - }); - const subtreeSiblingPathAsFields = newSubtreeSiblingPath.toFields(); - const newPublicDataSubtreeSiblingPath = makeTuple(PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, i => { - return subtreeSiblingPathAsFields[i]; - }); + const lowPublicDataWritesPreimages = []; + const lowPublicDataWritesMembershipWitnesses = []; + const publicDataWritesSiblingPaths = []; + + for (const write of allPublicDataWrites) { + if (write.isEmpty()) { + throw new Error(`Empty public data write in tx: ${toFriendlyJSON(tx)}`); + } + + // TODO(Alvaro) write a specialized function for this? Internally add_or_update_value uses batch insertion anyway + const { lowLeavesWitnessData, newSubtreeSiblingPath } = await db.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + [write.toBuffer()], + // TODO(#3675) remove oldValue from update requests + 0, + ); - const lowPublicDataWritesMembershipWitnesses: Tuple< - MembershipWitness, - typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - > = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { - const witness = lowLeavesWitnessData[i]; - return MembershipWitness.fromBufferArray( - witness.index, - assertLength(witness.siblingPath.toBufferArray(), PUBLIC_DATA_TREE_HEIGHT), + if (lowLeavesWitnessData === undefined) { + throw new Error(`Could not craft public data batch insertion proofs`); + } + + const [lowLeafWitness] = lowLeavesWitnessData; + lowPublicDataWritesPreimages.push(lowLeafWitness.leafPreimage as PublicDataTreeLeafPreimage); + lowPublicDataWritesMembershipWitnesses.push( + MembershipWitness.fromBufferArray( + lowLeafWitness.index, + assertLength(lowLeafWitness.siblingPath.toBufferArray(), PUBLIC_DATA_TREE_HEIGHT), + ), ); - }); - const lowPublicDataWritesPreimages: Tuple< - PublicDataTreeLeafPreimage, - typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - > = makeTuple(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => { - return lowLeavesWitnessData[i].leafPreimage as PublicDataTreeLeafPreimage; - }); + const insertionSiblingPath = newSubtreeSiblingPath.toFields(); + assertLength(insertionSiblingPath, PUBLIC_DATA_TREE_HEIGHT); - // validate that the sortedPublicDataWrites and sortedPublicDataWritesIndexes are in the correct order - // otherwise it will just fail in the circuit - assertPermutation(allPublicDataWrites, sortedPublicDataWrites, sortedPublicDataWritesIndexes, (a, b) => a.equals(b)); + publicDataWritesSiblingPaths.push(insertionSiblingPath as Tuple); + } return { lowPublicDataWritesPreimages, lowPublicDataWritesMembershipWitnesses, - newPublicDataSubtreeSiblingPath, - sortedPublicDataWrites, - sortedPublicDataWritesIndexes, + publicDataWritesSiblingPaths, }; } diff --git a/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts b/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts index 00dde60f9b6..d3bcec2b7d5 100644 --- a/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/tx-proving-state.ts @@ -13,8 +13,10 @@ import { AvmProofData, type BaseRollupHints, Fr, + PrivateBaseRollupHints, PrivateBaseRollupInputs, PrivateTubeData, + PublicBaseRollupHints, PublicBaseRollupInputs, PublicTubeData, type TUBE_PROOF_LENGTH, @@ -66,6 +68,9 @@ export class TxProvingState { const vkData = this.getTubeVkData(); const tubeData = new PrivateTubeData(this.processedTx.data.toKernelCircuitPublicInputs(), this.tube.proof, vkData); + if (!(this.baseRollupHints instanceof PrivateBaseRollupHints)) { + throw new Error('Mismatched base rollup hints, expected private base rollup hints'); + } return new PrivateBaseRollupInputs(tubeData, this.baseRollupHints); } @@ -92,6 +97,10 @@ export class TxProvingState { this.getAvmVkData(), ); + if (!(this.baseRollupHints instanceof PublicBaseRollupHints)) { + throw new Error('Mismatched base rollup hints, expected public base rollup hints'); + } + return new PublicBaseRollupInputs(tubeData, avmProofData, this.baseRollupHints); } diff --git a/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts index de9828a3c70..8adc4bebe14 100644 --- a/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts @@ -2,6 +2,7 @@ import { BBNativeRollupProver, type BBProverConfig } from '@aztec/bb-prover'; import { makeEmptyProcessedTx } from '@aztec/circuit-types'; import { PRIVATE_KERNEL_EMPTY_INDEX, + PrivateBaseRollupHints, PrivateBaseRollupInputs, PrivateKernelEmptyInputData, PrivateTubeData, @@ -59,7 +60,7 @@ describe('prover/bb_prover/base-rollup', () => { const tubeData = new PrivateTubeData(tubeProof.inputs, tubeProof.proof, vkData); const baseRollupHints = await buildBaseRollupHints(tx, context.globalVariables, context.actualDb); - const baseRollupInputs = new PrivateBaseRollupInputs(tubeData, baseRollupHints); + const baseRollupInputs = new PrivateBaseRollupInputs(tubeData, baseRollupHints as PrivateBaseRollupHints); logger.verbose('Proving base rollups'); const proofOutputs = await context.prover.getPrivateBaseRollupProof(baseRollupInputs); diff --git a/yarn-project/sequencer-client/src/block_builder/light.test.ts b/yarn-project/sequencer-client/src/block_builder/light.test.ts index ebb0dbcf9ad..43cbd91a83b 100644 --- a/yarn-project/sequencer-client/src/block_builder/light.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/light.test.ts @@ -23,6 +23,7 @@ import { NUM_BASE_PARITY_PER_ROOT_PARITY, type ParityPublicInputs, PreviousRollupData, + type PrivateBaseRollupHints, PrivateBaseRollupInputs, PrivateTubeData, type RecursiveProof, @@ -268,7 +269,7 @@ describe('LightBlockBuilder', () => { const vkData = new VkWitnessData(TubeVk, vkIndex, vkPath); const tubeData = new PrivateTubeData(tx.data.toKernelCircuitPublicInputs(), emptyProof, vkData); const hints = await buildBaseRollupHints(tx, globalVariables, expectsFork); - const inputs = new PrivateBaseRollupInputs(tubeData, hints); + const inputs = new PrivateBaseRollupInputs(tubeData, hints as PrivateBaseRollupHints); const result = await simulator.getPrivateBaseRollupProof(inputs); rollupOutputs.push(result.inputs); } diff --git a/yarn-project/world-state/src/native/native_world_state.ts b/yarn-project/world-state/src/native/native_world_state.ts index 7be252683e1..a49c2739b4d 100644 --- a/yarn-project/world-state/src/native/native_world_state.ts +++ b/yarn-project/world-state/src/native/native_world_state.ts @@ -149,16 +149,19 @@ export class NativeWorldStateService implements MerkleTreeDatabase { // We insert the public data tree leaves with one batch per tx to avoid updating the same key twice const batchesOfPaddedPublicDataWrites: PublicDataTreeLeaf[][] = []; for (const txEffect of paddedTxEffects) { - const batch: PublicDataTreeLeaf[] = Array(MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX).fill( - PublicDataTreeLeaf.empty(), + batchesOfPaddedPublicDataWrites.push( + txEffect.publicDataWrites.map(write => { + if (write.isEmpty()) { + console.log(txEffect); + throw new Error('Public data write must not be empty when syncing'); + } + return new PublicDataTreeLeaf(write.leafSlot, write.value); + }), ); - for (const [i, write] of txEffect.publicDataWrites.entries()) { - batch[i] = new PublicDataTreeLeaf(write.leafSlot, write.value); - } - - batchesOfPaddedPublicDataWrites.push(batch); } + console.log(batchesOfPaddedPublicDataWrites); + const response = await this.instance.call(WorldStateMessageType.SYNC_BLOCK, { blockNumber: l2Block.number, blockHeaderHash: l2Block.header.hash(), From 26e1d07111b1fa6f675602a03e556f5e56259760 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 15 Nov 2024 11:08:47 +0000 Subject: [PATCH 05/25] remove prints --- .../cached_content_addressed_tree_store.hpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp index bffe4881d49..ce7cbca77dc 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp @@ -638,14 +638,8 @@ template void ContentAddressedCachedTreeStore void ContentAddressedCachedTreeStore(uncommittedMeta.root), 0, *tx); - std::cout << "Persisted root" << std::endl; } if (asBlock) { ++uncommittedMeta.unfinalisedBlockHeight; From 93a181495085b629cdd4713192b03c414b7cec03 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 15 Nov 2024 11:40:47 +0000 Subject: [PATCH 06/25] fix inputvalue typing --- noir/noir-repo/tooling/noir_js_types/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir/noir-repo/tooling/noir_js_types/src/types.ts b/noir/noir-repo/tooling/noir_js_types/src/types.ts index 03e2a93f851..0cfe5d05a17 100644 --- a/noir/noir-repo/tooling/noir_js_types/src/types.ts +++ b/noir/noir-repo/tooling/noir_js_types/src/types.ts @@ -1,5 +1,5 @@ export type Field = string | number | boolean; -export type InputValue = Field | InputMap | (Field | InputMap)[]; +export type InputValue = Field | InputMap | InputValue[]; export type InputMap = { [key: string]: InputValue }; export type Visibility = 'public' | 'private' | 'databus'; From 3bb783ebf09a471bc5d277b68a294bf562087f27 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 15 Nov 2024 14:44:31 +0000 Subject: [PATCH 07/25] wip fix noir tests --- .../rollup-lib/src/base/components/mod.nr | 2 - .../src/base/components/private_tube_data.nr | 23 - .../src/base/private_base_rollup.nr | 797 +++++++++++++++++- .../rollup-lib/src/base/public_base_rollup.nr | 2 +- .../crates/types/src/abis/mod.nr | 1 + .../src/abis/tube.nr} | 24 +- .../crates/types/src/tests/fixture_builder.nr | 13 + .../structs/rollup/state_diff_hints.test.ts | 1 - .../src/test/bb_prover_base_rollup.test.ts | 2 +- 9 files changed, 833 insertions(+), 32 deletions(-) delete mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data.nr rename noir-projects/noir-protocol-circuits/crates/{rollup-lib/src/base/components/public_tube_data.nr => types/src/abis/tube.nr} (50%) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr index 352897aa409..719f03148a9 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr @@ -1,6 +1,4 @@ pub mod avm_proof_data; -pub mod private_tube_data; -pub mod public_tube_data; pub(crate) mod public_data_tree; pub(crate) mod nullifier_tree; pub(crate) mod fees; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data.nr deleted file mode 100644 index 74eac402aee..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data.nr +++ /dev/null @@ -1,23 +0,0 @@ -use dep::types::{ - abis::kernel_circuit_public_inputs::KernelCircuitPublicInputs, - constants::HONK_VERIFICATION_KEY_LENGTH_IN_FIELDS, - proof::{traits::Verifiable, tube_proof::TubeProof, vk_data::VkData}, -}; - -pub struct PrivateTubeData { - public_inputs: KernelCircuitPublicInputs, - proof: TubeProof, - vk_data: VkData, -} - -impl Verifiable for PrivateTubeData { - fn verify(self) { - let inputs = KernelCircuitPublicInputs::serialize(self.public_inputs); - std::verify_proof( - self.vk_data.vk.key, - self.proof.fields, - inputs, - self.vk_data.vk.hash, - ); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 43990f50b52..59fe9cc9626 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -6,8 +6,7 @@ use crate::{ base::{ components::{ fees::compute_fee_payer_fee_juice_balance_leaf_slot, - nullifier_tree::nullifier_tree_batch_insert, private_tube_data::PrivateTubeData, - public_data_tree::public_data_tree_insert, + nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, }, state_diff_hints::PrivateBaseStateDiffHints, }, @@ -17,6 +16,7 @@ use dep::types::{ abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, + tube::PrivateTubeData, }, constants::{ ARCHIVE_HEIGHT, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, @@ -266,3 +266,796 @@ impl PrivateBaseRollupInputs { } } +mod tests { + use crate::{ + abis::{ + base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, + constant_rollup_data::ConstantRollupData, + }, + base::{ + components::fees::compute_fee_payer_fee_juice_balance_leaf_slot, + private_base_rollup::PrivateBaseRollupInputs, + state_diff_hints::PrivateBaseStateDiffHints, + }, + components::TX_EFFECTS_HASH_INPUT_FIELDS, + }; + use dep::types::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, gas::Gas, gas_fees::GasFees, + kernel_circuit_public_inputs::KernelCircuitPublicInputs, + nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, + }, + address::{AztecAddress, EthAddress}, + constants::{ + ARCHIVE_HEIGHT, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + NOTE_HASH_SUBTREE_HEIGHT, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NOTE_HASH_TREE_HEIGHT, + NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, + PRIVATE_KERNEL_EMPTY_INDEX, PRIVATE_KERNEL_TAIL_INDEX, + PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, + }, + data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, + hash::silo_l2_to_l1_message, + merkle_tree::MembershipWitness, + messaging::l2_to_l1_message::ScopedL2ToL1Message, + partial_state_reference::PartialStateReference, + tests::{fixture_builder::FixtureBuilder, fixtures, merkle_tree_utils::NonEmptyMerkleTree}, + traits::{Empty, is_empty}, + utils::{ + arrays::get_sorted_tuple::get_sorted_tuple, + field::{field_from_bytes_32_trunc, full_field_less_than}, + }, + }; + + struct NullifierInsertion { + existing_index: u32, + value: Field, + } + + global MAX_nullifiers_PER_TEST: u32 = 4; + global AVAILABLE_PUBLIC_DATA_LEAVES_FOR_TEST = 64; + global AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST = 6; + + fn update_public_data_tree( + public_data_tree: &mut NonEmptyMerkleTree, + snapshot: AppendOnlyTreeSnapshot, + fee_write: (u32, PublicDataTreeLeaf), + mut pre_existing_public_data: [PublicDataTreeLeafPreimage; EXISTING_LEAVES], + ) -> (PublicDataTreeLeafPreimage, MembershipWitness, [Field; PUBLIC_DATA_TREE_HEIGHT]) { + let (low_leaf_index, fee_write) = fee_write; + if (!is_empty(fee_write)) { + let low_leaf = pre_existing_public_data[low_leaf_index]; + let mut new_leaf = PublicDataTreeLeafPreimage::empty(); + if low_leaf.slot == new_leaf.slot { + pre_existing_public_data[low_leaf_index].value = fee_write.value; + } else { + new_leaf = PublicDataTreeLeafPreimage { + slot: fee_write.slot, + value: fee_write.value, + next_slot: low_leaf.next_slot, + next_index: low_leaf.next_index, + }; + pre_existing_public_data[low_leaf_index] = PublicDataTreeLeafPreimage { + slot: low_leaf.slot, + value: low_leaf.value, + next_slot: fee_write.slot, + next_index: MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + }; + } + let low_public_data_writes_witness = MembershipWitness { + leaf_index: low_leaf_index as Field, + sibling_path: public_data_tree.get_sibling_path(low_leaf_index), + }; + + public_data_tree.update_leaf( + low_leaf_index, + pre_existing_public_data[low_leaf_index].hash(), + ); + + let insertion_witness = + public_data_tree.get_sibling_path(snapshot.next_available_leaf_index); + + public_data_tree.update_leaf(snapshot.next_available_leaf_index, new_leaf.hash()); + + (low_leaf, low_public_data_writes_witness, insertion_witness) + } else { + ( + PublicDataTreeLeafPreimage::empty(), MembershipWitness::empty(), + [0; PUBLIC_DATA_TREE_HEIGHT], + ) + } + } + + struct PrivateBaseRollupInputsBuilder { + tube_data: FixtureBuilder, + pre_existing_notes: [Field; MAX_NOTE_HASHES_PER_TX], + pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], + pre_existing_contracts: [Field; 2], + pre_existing_public_data: [PublicDataTreeLeafPreimage; 10], + pre_existing_blocks: [Field; 2], + fee_write: (u32, PublicDataTreeLeaf), + + nullifiers: BoundedVec, + constants: ConstantRollupData, + // Index of the item in the pre_existing_public_data array that contains the fee payer's Fee Juice balance. + // Used for building the public data hint read for the payment update request. If set to none, no hint is built. + fee_payer_fee_juice_balance_pre_existing_public_data_index: Option, + } + + impl PrivateBaseRollupInputsBuilder { + fn new() -> Self { + let mut inputs = PrivateBaseRollupInputsBuilder::empty(); + inputs.tube_data = FixtureBuilder::new().in_vk_tree(PRIVATE_KERNEL_TAIL_INDEX); + inputs.constants.global_variables.chain_id = fixtures::CHAIN_ID; + inputs.constants.global_variables.version = fixtures::VERSION; + inputs.constants.vk_tree_root = inputs.tube_data.vk_tree_root; + + inputs.pre_existing_blocks[0] = inputs.tube_data.historical_header.hash(); + + inputs + } + + unconstrained fn new_with_previous_kernel(previous_vk_index: u32) -> Self { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.tube_data = builder.tube_data.in_vk_tree(previous_vk_index); + builder + } + + fn build_fee_payer_fee_juice_balance_read_hint( + self, + start_public_data_tree: NonEmptyMerkleTree, + ) -> PublicDataHint { + self.fee_payer_fee_juice_balance_pre_existing_public_data_index.map_or( + PublicDataHint::empty(), + |leaf_index_u32: u32| { + let leaf_index = leaf_index_u32 as Field; + let leaf_preimage = self.pre_existing_public_data[leaf_index]; + let membership_witness = MembershipWitness { + leaf_index, + sibling_path: start_public_data_tree.get_sibling_path(leaf_index_u32), + }; + PublicDataHint { + leaf_slot: leaf_preimage.slot, + value: leaf_preimage.value, + override_counter: 0, + membership_witness, + leaf_preimage, + } + }, + ) + } + + fn extract_subtree_sibling_path( + path: [Field; FULL_HEIGHT], + mut sibling_path: [Field; SIBLING_PATH_LENGTH], + ) -> [Field; SIBLING_PATH_LENGTH] { + let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; + for i in subtree_height..FULL_HEIGHT { + sibling_path[i - subtree_height] = path[i]; + } + sibling_path + } + + fn update_nullifier_tree_with_new_leaves( + mut self, + nullifier_tree: &mut NonEmptyMerkleTree, + kernel_public_inputs: &mut KernelCircuitPublicInputs, + start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot, + ) -> ([NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], [MembershipWitness; MAX_NULLIFIERS_PER_TX], [Field; MAX_NULLIFIERS_PER_TX], [u32; MAX_NULLIFIERS_PER_TX]) { + let mut nullifier_predecessor_preimages = + [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX]; + let mut low_nullifier_membership_witness = + [MembershipWitness::empty(); MAX_NULLIFIERS_PER_TX]; + + let sorted_new_nullifier_tuples = unsafe { + get_sorted_tuple( + self.nullifiers.storage.map(|insertion: NullifierInsertion| insertion.value), + |a, b| full_field_less_than(b, a), + ) + }; + + let mut sorted_nullifiers = [0; MAX_NULLIFIERS_PER_TX]; + let mut sorted_nullifiers_indexes = [0; MAX_NULLIFIERS_PER_TX]; + + for i in 0..MAX_NULLIFIERS_PER_TX { + if (i as u32) < (MAX_nullifiers_PER_TEST as u32) { + sorted_nullifiers[i] = sorted_new_nullifier_tuples[i].elem; + sorted_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; + } else { + sorted_nullifiers[i] = 0; + sorted_nullifiers_indexes[i] = i; + } + } + + let mut pre_existing_nullifiers = self.pre_existing_nullifiers; + + for i in 0..MAX_nullifiers_PER_TEST { + if i < self.nullifiers.len() { + let sorted_tuple = sorted_new_nullifier_tuples[i]; + let new_nullifier = sorted_tuple.elem; + let original_index = sorted_tuple.original_index; + + let low_index = self.nullifiers.get_unchecked(original_index).existing_index; + + kernel_public_inputs.end.nullifiers[original_index] = new_nullifier; + + let mut low_preimage = pre_existing_nullifiers[low_index]; + nullifier_predecessor_preimages[i] = low_preimage; + low_nullifier_membership_witness[i] = MembershipWitness { + leaf_index: low_index as Field, + sibling_path: nullifier_tree.get_sibling_path(low_index), + }; + + low_preimage.next_nullifier = new_nullifier; + low_preimage.next_index = start_nullifier_tree_snapshot + .next_available_leaf_index as u32 + + original_index; + pre_existing_nullifiers[low_index] = low_preimage; + + nullifier_tree.update_leaf(low_index, low_preimage.hash()); + } + } + + ( + nullifier_predecessor_preimages, low_nullifier_membership_witness, + sorted_nullifiers, sorted_nullifiers_indexes, + ) + } + + unconstrained fn build_inputs(mut self) -> PrivateBaseRollupInputs { + let mut tube_data = self.tube_data.to_private_tube_data(); + + let start_note_hash_tree = NonEmptyMerkleTree::new( + self.pre_existing_notes, + [0; NOTE_HASH_TREE_HEIGHT], + [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], + [0; NOTE_HASH_SUBTREE_HEIGHT], + ); + let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_note_hash_tree.get_root(), + next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, + }; + let note_hash_subtree_sibling_path = PrivateBaseRollupInputsBuilder::extract_subtree_sibling_path( + start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), + [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], + ); + + let mut start_nullifier_tree = NonEmptyMerkleTree::new( + self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], + [0; NULLIFIER_SUBTREE_HEIGHT], + ); + + let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_nullifier_tree.get_root(), + next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, + }; + + let mut pre_existing_leaves = [0; AVAILABLE_PUBLIC_DATA_LEAVES_FOR_TEST]; + + for i in 0..self.pre_existing_public_data.len() { + pre_existing_leaves[i] = self.pre_existing_public_data[i].hash(); + } + + let mut start_public_data_tree = NonEmptyMerkleTree::new( + pre_existing_leaves, + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST], + [0; AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST], + ); + let start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_public_data_tree.get_root(), + next_available_leaf_index: self.pre_existing_public_data.len(), + }; + + let fee_payer_fee_juice_balance_read_hint = + self.build_fee_payer_fee_juice_balance_read_hint(start_public_data_tree); + + let start_archive = NonEmptyMerkleTree::new( + self.pre_existing_blocks, + [0; ARCHIVE_HEIGHT], + [0; ARCHIVE_HEIGHT - 1], + [0; 1], + ); + self.constants.last_archive = AppendOnlyTreeSnapshot { + root: start_archive.get_root(), + next_available_leaf_index: start_archive.get_next_available_index() as u32, + }; + + let (nullifier_predecessor_preimages, nullifier_predecessor_membership_witnesses, sorted_nullifiers, sorted_nullifier_indexes) = self + .update_nullifier_tree_with_new_leaves( + &mut start_nullifier_tree, + &mut tube_data.public_inputs, + start_nullifier_tree_snapshot, + ); + + let nullifier_subtree_sibling_path = PrivateBaseRollupInputsBuilder::extract_subtree_sibling_path( + start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), + [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + ); + + let (fee_write_low_leaf_preimage, fee_write_low_leaf_membership_witness, fee_write_sibling_path) = update_public_data_tree( + &mut start_public_data_tree, + start_public_data_tree_snapshot, + self.fee_write, + self.pre_existing_public_data, + ); + + let start = PartialStateReference { + note_hash_tree: start_note_hash_tree_snapshot, + nullifier_tree: start_nullifier_tree_snapshot, + public_data_tree: start_public_data_tree_snapshot, + }; + + let state_diff_hints = PrivateBaseStateDiffHints { + nullifier_predecessor_preimages, + nullifier_predecessor_membership_witnesses, + sorted_nullifiers, + sorted_nullifier_indexes, + note_hash_subtree_sibling_path, + nullifier_subtree_sibling_path, + fee_write_low_leaf_preimage, + fee_write_low_leaf_membership_witness, + fee_write_sibling_path, + }; + + PrivateBaseRollupInputs { + tube_data, + start, + state_diff_hints, + archive_root_membership_witness: MembershipWitness { + leaf_index: 0, + sibling_path: start_archive.get_sibling_path(0), + }, + constants: self.constants, + fee_payer_fee_juice_balance_read_hint, + } + } + + fn execute(self) -> BaseOrMergeRollupPublicInputs { + let inputs = unsafe { self.build_inputs() }; + inputs.execute() + } + + fn succeeds(self) { + let _ = self.execute(); + } + + fn fails(self) { + let _ = self.execute(); + } + } + + impl Empty for PrivateBaseRollupInputsBuilder { + fn empty() -> Self { + Self { + tube_data: FixtureBuilder::new(), + pre_existing_notes: [0; MAX_NOTE_HASHES_PER_TX], + pre_existing_nullifiers: [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX], + pre_existing_contracts: [0; 2], + pre_existing_public_data: [PublicDataTreeLeafPreimage::empty(); 10], + fee_write: (0, PublicDataTreeLeaf::empty()), + pre_existing_blocks: [0; 2], + nullifiers: BoundedVec::new(), + constants: ConstantRollupData::empty(), + fee_payer_fee_juice_balance_pre_existing_public_data_index: Option::none(), + } + } + } + + #[test] + unconstrained fn note_hashes_tree() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + let note_hashes = [27, 28, 29, 30, 31, 32]; + for i in 0..note_hashes.len() { + builder.tube_data.add_new_note_hash(note_hashes[i]); + } + let mut expected_commitments_tree = NonEmptyMerkleTree::new( + [0; MAX_NOTE_HASHES_PER_TX * 2], + [0; NOTE_HASH_TREE_HEIGHT], + [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], + [0; NOTE_HASH_SUBTREE_HEIGHT + 1], + ); + + let outputs = builder.execute(); + let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: expected_commitments_tree.get_root(), + next_available_leaf_index: MAX_NOTE_HASHES_PER_TX as u32, + }; + assert(outputs.start.note_hash_tree.eq(expected_start_note_hash_tree_snapshot)); + + for i in 0..note_hashes.len() { + expected_commitments_tree.update_leaf(i + MAX_NOTE_HASHES_PER_TX, note_hashes[i]); + } + let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: expected_commitments_tree.get_root(), + next_available_leaf_index: (MAX_NOTE_HASHES_PER_TX * 2) as u32, + }; + assert(outputs.end.note_hash_tree.eq(expected_end_note_hash_tree_snapshot)); + } + + #[test] + unconstrained fn new_nullifier_tree_empty() { + // This test checks for insertions of all 0 values + // In this special case we will not need to provide sibling paths to check insertion of the nullifier values + // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left + // empty to begin with. + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.succeeds(); + } + + #[test] + unconstrained fn nullifier_insertion_test() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 0, value: 1 }); + let mut tree_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX * 2]; + tree_nullifiers[0] = NullifierLeafPreimage { + nullifier: 0, + next_nullifier: 1, + next_index: MAX_NULLIFIERS_PER_TX, + }; + tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; + tree_nullifiers[MAX_NULLIFIERS_PER_TX] = + NullifierLeafPreimage { nullifier: 1, next_nullifier: 7, next_index: 1 }; + + let mut end_nullifier_tree = NonEmptyMerkleTree::new( + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + [0; NULLIFIER_SUBTREE_HEIGHT + 1], + ); + + let output = builder.execute(); + + assert(output.end.nullifier_tree.eq( + AppendOnlyTreeSnapshot { + root: end_nullifier_tree.get_root(), + next_available_leaf_index: 2 * MAX_NULLIFIERS_PER_TX as u32, + }, + )); + } + + #[test] + unconstrained fn new_nullifier_tree_all_larger() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + for i in 1..builder.nullifiers.max_len() { + builder.nullifiers.push( + NullifierInsertion { existing_index: 1, value: (8 + i) as Field }, + ); + } + + let output = builder.execute(); + let mut tree_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX * 2]; + tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; + + tree_nullifiers[1] = NullifierLeafPreimage { + nullifier: 7, + next_nullifier: 8, + next_index: MAX_NULLIFIERS_PER_TX, + }; + + let last_index = builder.nullifiers.max_len() - 1; + for i in 0..last_index { + tree_nullifiers[MAX_NULLIFIERS_PER_TX + i] = NullifierLeafPreimage { + nullifier: (8 + i) as Field, + next_nullifier: (8 + i + 1) as Field, + next_index: MAX_NULLIFIERS_PER_TX + i + 1, + }; + } + tree_nullifiers[MAX_NULLIFIERS_PER_TX + last_index] = NullifierLeafPreimage { + nullifier: (8 + last_index) as Field, + next_nullifier: 0, + next_index: 0, + }; + + let mut end_nullifier_tree = NonEmptyMerkleTree::new( + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + [0; NULLIFIER_SUBTREE_HEIGHT + 1], + ); + + assert(output.end.nullifier_tree.eq( + AppendOnlyTreeSnapshot { + root: end_nullifier_tree.get_root(), + next_available_leaf_index: 2 * MAX_NULLIFIERS_PER_TX as u32, + }, + )); + } + + #[test(should_fail_with = "Invalid low leaf")] + unconstrained fn new_nullifier_tree_double_spend() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + + builder.fails(); + } + + #[test(should_fail_with = "Invalid low leaf")] + unconstrained fn new_nullifier_tree_double_spend_same_batch() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + + builder.fails(); + } + + #[test] + unconstrained fn empty_tx_effects_hash() { + let outputs = PrivateBaseRollupInputsBuilder::new().execute(); + + let hash_input_flattened = [0; TX_EFFECTS_HASH_INPUT_FIELDS * 32]; + let sha_digest = std::hash::sha256(hash_input_flattened); + let expected_tx_effects_hash = field_from_bytes_32_trunc(sha_digest); + assert_eq(outputs.txs_effects_hash, expected_tx_effects_hash); + } + + #[test] + unconstrained fn empty_block_out_hash() { + let outputs = PrivateBaseRollupInputsBuilder::new().execute(); + assert_eq(outputs.out_hash, 0); + } + + #[test] + unconstrained fn nonempty_block_out_hash() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + for i in 0..MAX_L2_TO_L1_MSGS_PER_TX { + builder.tube_data.add_exposed_l2_to_l1_message( + i as Field, + EthAddress::from_field(1 + i as Field), + ); + } + + let out_hash = builder.execute().out_hash; + let siloed_l2_to_l1_msgs = builder.tube_data.l2_to_l1_msgs.map( + |l2_to_l1_message: ScopedL2ToL1Message| silo_l2_to_l1_message( + l2_to_l1_message, + builder.constants.global_variables.version, + builder.constants.global_variables.chain_id, + ), + ); + + // Since we fill the tree completely, we know to expect a full tree as below + let expected_tree = dep::types::merkle_tree::variable_merkle_tree::tests::generate_full_sha_tree( + siloed_l2_to_l1_msgs.storage(), + ); + assert_eq(out_hash, expected_tree.get_root()); + } + + #[test(should_fail_with = "membership check failed")] + unconstrained fn compute_membership_archive_negative() { + let mut inputs = PrivateBaseRollupInputsBuilder::new().build_inputs(); + inputs.archive_root_membership_witness.sibling_path[0] = 27; + let _output = inputs.execute(); + } + + #[test] + unconstrained fn constants_dont_change() { + let inputs = PrivateBaseRollupInputsBuilder::new().build_inputs(); + let outputs = inputs.execute(); + + assert(inputs.constants.eq(outputs.constants)); + } + + #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] + unconstrained fn constants_dont_match_kernels_chain_id() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.constants.global_variables.chain_id = 3; + builder.fails(); + } + + #[test(should_fail_with = "kernel version does not match the rollup version")] + unconstrained fn constants_dont_match_kernels_version() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.constants.global_variables.version += 1; + builder.fails(); + } + + #[test(should_fail_with = "kernel global variables do not match the rollup global variables")] + unconstrained fn constants_global_variables_dont_match_kernels() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.tube_data.global_variables.block_number = 6; + builder.constants.global_variables.block_number = 7; + builder.fails(); + } + + #[test(should_fail_with = "kernel max_block_number is smaller than block number")] + unconstrained fn constants_dont_satisfy_smaller_max_block_number() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.constants.global_variables.block_number = 42; + builder.tube_data.set_max_block_number(5); + builder.fails(); + } + + #[test] + unconstrained fn constants_satisfy_equal_max_block_number() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.constants.global_variables.block_number = 42; + builder.tube_data.set_max_block_number(42); + builder.succeeds(); + } + + #[test] + unconstrained fn constants_satisfy_larger_max_block_number() { + let mut builder = PrivateBaseRollupInputsBuilder::new(); + builder.constants.global_variables.block_number = 42; + builder.tube_data.set_max_block_number(4294967295); + builder.succeeds(); + } + + #[test] + unconstrained fn num_txs_is_1() { + let outputs = PrivateBaseRollupInputsBuilder::new().execute(); + + assert_eq(outputs.num_txs, 1); + } + + #[test] + unconstrained fn updates_fee_payer_balance_with_new_data_write() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + let initial_balance = 300_000; + let gas_fees = GasFees { fee_per_da_gas: 1, fee_per_l2_gas: 2 }; + let gas_used = Gas::new(50_000, 25_000); + let expected_balance = 200_000; + + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + // Set gas + builder.tube_data.gas_used = gas_used; + builder.constants.global_variables.gas_fees = gas_fees; + + // Set fee payer + builder.tube_data.fee_payer = fee_payer; + + // Set pre-existing balance + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); + + // Set expected protocol data update + builder.fee_write = (0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }); + + let outputs = builder.execute(); + + // The new public data tree should have updated the balance of the fee payer + let updated_leaf = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: expected_balance, + next_slot: 0, + next_index: 0, + }; + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + [updated_leaf.hash(), 0], + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - 1], + [0; 1], + ); + + assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); + } + + // #[test(should_fail_with = "Not enough balance for fee payer to pay for transaction")] + // unconstrained fn fails_to_update_fee_payer_balance_if_not_enough_funds() { + // let fee_payer = AztecAddress::from_field(0x1234); + // let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + // // Set low initial balance so it fails! + // let initial_balance = 10_000; + // let tx_fee = 100_000; + + // let mut builder = PrivateBaseRollupInputsBuilder::new(); + + // // Set fee payer + // builder.tube_data.fee_payer = fee_payer; + + // // Set pre-existing balance + // builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + // slot: balance_slot, + // value: initial_balance, + // next_slot: 0, + // next_index: 0, + // }; + // builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); + + // // Set values for computing exact tx_fee + // builder.transaction_fee = tx_fee; + + // // Set expected protocol data update + // builder.protocol_public_data_writes.push(( + // 0, PublicDataTreeLeaf { slot: balance_slot, value: -90_000 }, + // )); + + // builder.fails(); + // } + + // #[test(should_fail_with = "Wrong leaf slot for Fee Juice balance read hint")] + // unconstrained fn fails_to_update_fee_payer_balance_if_wrong_read_hint() { + // let fee_payer = AztecAddress::from_field(0x1234); + // let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + // let initial_balance = 300_000; + // let expected_balance = 200_000; + // let tx_fee = 100_000; + + // let mut builder = PrivateBaseRollupInputsBuilder::new(); + + // // Set fee payer + // builder.kernel_data.fee_payer = fee_payer; + + // // Set pre-existing balance in index 0 + // builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + // slot: balance_slot, + // value: initial_balance, + // next_slot: 0, + // next_index: 0, + // }; + + // builder.pre_existing_public_data[1] = PublicDataTreeLeafPreimage { + // slot: 1, + // value: initial_balance, + // next_slot: balance_slot, + // next_index: 0, + // }; + + // // But point the read hint to the wrong one! + // builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(1); + + // // Set values for computing exact tx_fee + // builder.transaction_fee = tx_fee; + + // // Set expected protocol data update + // builder.protocol_public_data_writes.push(( + // 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, + // )); + + // builder.fails(); + // } + + #[test] + fn valid_previous_kernel_empty() { + let builder = unsafe { + PrivateBaseRollupInputsBuilder::new_with_previous_kernel(PRIVATE_KERNEL_EMPTY_INDEX) + }; + + let _res = builder.execute(); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index a7034062592..340c7d45640 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -7,7 +7,6 @@ use crate::{ components::{ avm_proof_data::AvmProofData, fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, - public_tube_data::PublicTubeData, }, state_diff_hints::PublicBaseStateDiffHints, }, @@ -22,6 +21,7 @@ use dep::types::{ log_hash::{LogHash, ScopedLogHash}, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, + tube::PublicTubeData, }, constants::{ ARCHIVE_HEIGHT, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr index e29706c42ed..1365fff4b8a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr @@ -49,3 +49,4 @@ pub mod gas_settings; pub mod gas; pub mod tree_snapshots; +pub mod tube; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr similarity index 50% rename from noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr index 3fe33cc8734..814a5bfdd7e 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr @@ -1,5 +1,7 @@ -use dep::types::{ - abis::kernel_circuit_public_inputs::PrivateToPublicKernelCircuitPublicInputs, +use crate::{ + abis::kernel_circuit_public_inputs::{ + KernelCircuitPublicInputs, PrivateToPublicKernelCircuitPublicInputs, + }, constants::HONK_VERIFICATION_KEY_LENGTH_IN_FIELDS, proof::{traits::Verifiable, tube_proof::TubeProof, vk_data::VkData}, }; @@ -21,3 +23,21 @@ impl Verifiable for PublicTubeData { ); } } + +pub struct PrivateTubeData { + public_inputs: KernelCircuitPublicInputs, + proof: TubeProof, + vk_data: VkData, +} + +impl Verifiable for PrivateTubeData { + fn verify(self) { + let inputs = KernelCircuitPublicInputs::serialize(self.public_inputs); + std::verify_proof( + self.vk_data.vk.key, + self.proof.fields, + inputs, + self.vk_data.vk.hash, + ); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 96fd4a771d6..c94922d6671 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -34,6 +34,7 @@ use crate::{ read_request::{ReadRequest, ScopedReadRequest}, side_effect::Counted, tree_leaf_read_request::TreeLeafReadRequest, + tube::{PrivateTubeData, PublicTubeData}, tx_constant_data::TxConstantData, validation_requests::{ KeyValidationRequest, KeyValidationRequestAndGenerator, PrivateValidationRequests, @@ -1239,6 +1240,18 @@ impl FixtureBuilder { fn vk_tree_root() -> Field { fixtures::vk_tree::get_vk_merkle_tree().get_root() } + + fn to_private_tube_data(self) -> PrivateTubeData { + let mut result: PrivateTubeData = std::mem::zeroed(); + result.public_inputs = self.to_kernel_circuit_public_inputs(); + result + } + + fn to_public_tube_data(self) -> PublicTubeData { + let mut result: PublicTubeData = std::mem::zeroed(); + result.public_inputs = self.to_private_to_public_kernel_circuit_public_inputs(true); + result + } } impl Empty for FixtureBuilder { diff --git a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts index c5a6d3ad325..a473a1375fa 100644 --- a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts +++ b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.test.ts @@ -9,7 +9,6 @@ describe('StateDiffHints', () => { expect(res).toEqual(expected); }); - it('serializes public hints to buffer and deserializes it back', () => { const expected = makePublicBaseStateDiffHints(); const buffer = expected.toBuffer(); diff --git a/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts index 8adc4bebe14..e8b644a8a26 100644 --- a/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts @@ -2,7 +2,7 @@ import { BBNativeRollupProver, type BBProverConfig } from '@aztec/bb-prover'; import { makeEmptyProcessedTx } from '@aztec/circuit-types'; import { PRIVATE_KERNEL_EMPTY_INDEX, - PrivateBaseRollupHints, + type PrivateBaseRollupHints, PrivateBaseRollupInputs, PrivateKernelEmptyInputData, PrivateTubeData, From f0fade5861ee7f089d653ad154f6f70ddb36038e Mon Sep 17 00:00:00 2001 From: sirasistant Date: Fri, 15 Nov 2024 15:13:08 +0000 Subject: [PATCH 08/25] fix private base rollup tests --- .../src/base/private_base_rollup.nr | 147 +++++++++--------- 1 file changed, 72 insertions(+), 75 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 59fe9cc9626..a557d8810ff 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -327,7 +327,7 @@ mod tests { if (!is_empty(fee_write)) { let low_leaf = pre_existing_public_data[low_leaf_index]; let mut new_leaf = PublicDataTreeLeafPreimage::empty(); - if low_leaf.slot == new_leaf.slot { + if low_leaf.slot == fee_write.slot { pre_existing_public_data[low_leaf_index].value = fee_write.value; } else { new_leaf = PublicDataTreeLeafPreimage { @@ -975,80 +975,77 @@ mod tests { assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); } - // #[test(should_fail_with = "Not enough balance for fee payer to pay for transaction")] - // unconstrained fn fails_to_update_fee_payer_balance_if_not_enough_funds() { - // let fee_payer = AztecAddress::from_field(0x1234); - // let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - // // Set low initial balance so it fails! - // let initial_balance = 10_000; - // let tx_fee = 100_000; - - // let mut builder = PrivateBaseRollupInputsBuilder::new(); - - // // Set fee payer - // builder.tube_data.fee_payer = fee_payer; - - // // Set pre-existing balance - // builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { - // slot: balance_slot, - // value: initial_balance, - // next_slot: 0, - // next_index: 0, - // }; - // builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); - - // // Set values for computing exact tx_fee - // builder.transaction_fee = tx_fee; - - // // Set expected protocol data update - // builder.protocol_public_data_writes.push(( - // 0, PublicDataTreeLeaf { slot: balance_slot, value: -90_000 }, - // )); - - // builder.fails(); - // } - - // #[test(should_fail_with = "Wrong leaf slot for Fee Juice balance read hint")] - // unconstrained fn fails_to_update_fee_payer_balance_if_wrong_read_hint() { - // let fee_payer = AztecAddress::from_field(0x1234); - // let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - // let initial_balance = 300_000; - // let expected_balance = 200_000; - // let tx_fee = 100_000; - - // let mut builder = PrivateBaseRollupInputsBuilder::new(); - - // // Set fee payer - // builder.kernel_data.fee_payer = fee_payer; - - // // Set pre-existing balance in index 0 - // builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { - // slot: balance_slot, - // value: initial_balance, - // next_slot: 0, - // next_index: 0, - // }; - - // builder.pre_existing_public_data[1] = PublicDataTreeLeafPreimage { - // slot: 1, - // value: initial_balance, - // next_slot: balance_slot, - // next_index: 0, - // }; - - // // But point the read hint to the wrong one! - // builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(1); - - // // Set values for computing exact tx_fee - // builder.transaction_fee = tx_fee; - - // // Set expected protocol data update - // builder.protocol_public_data_writes.push(( - // 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, - // )); - - // builder.fails(); - // } + #[test(should_fail_with = "Not enough balance for fee payer to pay for transaction")] + unconstrained fn fails_to_update_fee_payer_balance_if_not_enough_funds() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + // Set low initial balance so it fails! + let initial_balance = 10_000; + let gas_fees = GasFees { fee_per_da_gas: 1, fee_per_l2_gas: 2 }; + let gas_used = Gas::new(50_000, 25_000); + + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + // Set gas + builder.tube_data.gas_used = gas_used; + builder.constants.global_variables.gas_fees = gas_fees; + + // Set fee payer + builder.tube_data.fee_payer = fee_payer; + + // Set pre-existing balance + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); + + builder.fails(); + } + + #[test(should_fail_with = "Wrong leaf slot for Fee Juice balance read hint")] + unconstrained fn fails_to_update_fee_payer_balance_if_wrong_read_hint() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + let initial_balance = 300_000; + let expected_balance = 200_000; + let gas_fees = GasFees { fee_per_da_gas: 1, fee_per_l2_gas: 2 }; + let gas_used = Gas::new(50_000, 25_000); + + let mut builder = PrivateBaseRollupInputsBuilder::new(); + + // Set gas + builder.tube_data.gas_used = gas_used; + builder.constants.global_variables.gas_fees = gas_fees; + + // Set fee payer + builder.tube_data.fee_payer = fee_payer; + + // Set pre-existing balance in index 0 + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + + builder.pre_existing_public_data[1] = PublicDataTreeLeafPreimage { + slot: 1, + value: initial_balance, + next_slot: balance_slot, + next_index: 0, + }; + + // But point the read hint to the wrong one! + builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(1); + + // Set expected protocol data update + builder.fee_write = (0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }); + + builder.fails(); + } #[test] fn valid_previous_kernel_empty() { From 576a171534da78132db083e03b0ae6b5ce85288a Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 18 Nov 2024 09:23:18 +0000 Subject: [PATCH 09/25] wip public base rollup tests --- .../src/base/private_base_rollup.nr | 4 +- .../rollup-lib/src/base/public_base_rollup.nr | 883 +++++++++++++++++- 2 files changed, 882 insertions(+), 5 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index a557d8810ff..9ae9593f6c3 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -293,7 +293,7 @@ mod tests { NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, PRIVATE_KERNEL_EMPTY_INDEX, PRIVATE_KERNEL_TAIL_INDEX, PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, }, data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, hash::silo_l2_to_l1_message, @@ -386,7 +386,7 @@ mod tests { impl PrivateBaseRollupInputsBuilder { fn new() -> Self { let mut inputs = PrivateBaseRollupInputsBuilder::empty(); - inputs.tube_data = FixtureBuilder::new().in_vk_tree(PRIVATE_KERNEL_TAIL_INDEX); + inputs.tube_data = FixtureBuilder::new().in_vk_tree(TUBE_VK_INDEX); inputs.constants.global_variables.chain_id = fixtures::CHAIN_ID; inputs.constants.global_variables.version = fixtures::VERSION; inputs.constants.vk_tree_root = inputs.tube_data.vk_tree_root; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 340c7d45640..92c21f4d14f 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -142,9 +142,7 @@ impl PublicBaseRollupInputs { // self.avm_proof_data.vk_data.validate_in_vk_tree([AVM_VK_INDEX]); // } // TODO: Validate tube_data.public_inputs vs avm_proof_data.public_inputs - // TODO: Deprecate KernelData. - // Temporary workaround to create KernelCircuitPublicInputs from PublicKernelCircuitPublicInputs and AvmCircuitPublicInputs - // so that we don't have to modify base_rollup_inputs. + // TODO: Use both public avm and private inputs instead of merging them into one KernelCircuitPublicInputs let public_inputs = self.generate_kernel_circuit_public_inputs(); // Verify the kernel chain_id and versions @@ -415,3 +413,882 @@ impl PublicBaseRollupInputs { ); } } + +mod tests { + use crate::{ + abis::{ + base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, + constant_rollup_data::ConstantRollupData, + }, + base::{ + components::fees::compute_fee_payer_fee_juice_balance_leaf_slot, + public_base_rollup::PublicBaseRollupInputs, state_diff_hints::PublicBaseStateDiffHints, + }, + components::TX_EFFECTS_HASH_INPUT_FIELDS, + }; + use dep::types::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, + }, + address::{AztecAddress, EthAddress}, + constants::{ + ARCHIVE_HEIGHT, AVM_VK_INDEX, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, + MAX_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, + NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NOTE_HASH_TREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, + NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, + PRIVATE_KERNEL_EMPTY_INDEX, PRIVATE_KERNEL_TAIL_INDEX, + PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_SUBTREE_HEIGHT, + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, + }, + data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, + hash::silo_l2_to_l1_message, + merkle_tree::MembershipWitness, + messaging::l2_to_l1_message::ScopedL2ToL1Message, + partial_state_reference::PartialStateReference, + tests::{fixture_builder::FixtureBuilder, fixtures, merkle_tree_utils::NonEmptyMerkleTree}, + traits::Empty, + utils::{ + arrays::get_sorted_tuple::get_sorted_tuple, + field::{field_from_bytes_32_trunc, full_field_less_than}, + }, + }; + + struct NullifierInsertion { + existing_index: u32, + value: Field, + } + + global MAX_nullifiers_PER_TEST: u32 = 4; + global MAX_PUBLIC_DATA_READS_PER_TEST: u32 = 2; + + struct BaseRollupInputsBuilder { + tube_data: FixtureBuilder, + avm_data: FixtureBuilder, + pre_existing_notes: [Field; MAX_NOTE_HASHES_PER_TX], + pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], + pre_existing_contracts: [Field; 2], + pre_existing_public_data: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + pre_existing_blocks: [Field; 2], + // Public data writes generated by app code + public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, + // Public data writes to be created by the protocol (eg a data update request for updating fee payer balance) + protocol_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, + // Public data writes after processing by the base rollup circuit (defaults to public_data_writes ++ protocol_public_data_writes if empty) + final_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, + nullifiers: BoundedVec, + constants: ConstantRollupData, + // Index of the item in the pre_existing_public_data array that contains the fee payer's Fee Juice balance. + // Used for building the public data hint read for the payment update request. If set to none, no hint is built. + fee_payer_fee_juice_balance_pre_existing_public_data_index: Option, + } + + impl BaseRollupInputsBuilder { + fn new() -> Self { + let mut inputs = BaseRollupInputsBuilder::empty(); + inputs.tube_data = FixtureBuilder::new().in_vk_tree(TUBE_VK_INDEX); + inputs.avm_data = FixtureBuilder::new().in_vk_tree(AVM_VK_INDEX); + + inputs.constants.global_variables.chain_id = fixtures::CHAIN_ID; + inputs.constants.global_variables.version = fixtures::VERSION; + inputs.constants.vk_tree_root = inputs.tube_data.vk_tree_root; + + inputs.pre_existing_blocks[0] = inputs.tube_data.historical_header.hash(); + + inputs + } + + unconstrained fn new_with_previous_kernel(previous_vk_index: u32) -> Self { + let mut builder = BaseRollupInputsBuilder::new(); + builder.tube_data = builder.tube_data.in_vk_tree(previous_vk_index); + builder + } + + fn build_fee_payer_fee_juice_balance_read_hint( + self, + start_public_data_tree: NonEmptyMerkleTree, + ) -> PublicDataHint { + self.fee_payer_fee_juice_balance_pre_existing_public_data_index.map_or( + PublicDataHint::empty(), + |leaf_index_u32: u32| { + let leaf_index = leaf_index_u32 as Field; + let leaf_preimage = self.pre_existing_public_data[leaf_index]; + let membership_witness = MembershipWitness { + leaf_index, + sibling_path: start_public_data_tree.get_sibling_path(leaf_index_u32), + }; + PublicDataHint { + leaf_slot: leaf_preimage.slot, + value: leaf_preimage.value, + override_counter: 0, + membership_witness, + leaf_preimage, + } + }, + ) + } + + fn extract_subtree_sibling_path( + path: [Field; FULL_HEIGHT], + mut sibling_path: [Field; SIBLING_PATH_LENGTH], + ) -> [Field; SIBLING_PATH_LENGTH] { + let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; + for i in subtree_height..FULL_HEIGHT { + sibling_path[i - subtree_height] = path[i]; + } + sibling_path + } + + fn update_nullifier_tree_with_new_leaves( + mut self, + nullifier_tree: &mut NonEmptyMerkleTree, + kernel_data: &mut KernelData, + start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot, + ) -> ([NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], [MembershipWitness; MAX_NULLIFIERS_PER_TX], [Field; MAX_NULLIFIERS_PER_TX], [u32; MAX_NULLIFIERS_PER_TX]) { + let mut nullifier_predecessor_preimages = + [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX]; + let mut low_nullifier_membership_witness = + [MembershipWitness::empty(); MAX_NULLIFIERS_PER_TX]; + + let sorted_new_nullifier_tuples = unsafe { + get_sorted_tuple( + self.nullifiers.storage.map(|insertion: NullifierInsertion| insertion.value), + |a, b| full_field_less_than(b, a), + ) + }; + + let mut sorted_nullifiers = [0; MAX_NULLIFIERS_PER_TX]; + let mut sorted_nullifiers_indexes = [0; MAX_NULLIFIERS_PER_TX]; + + for i in 0..MAX_NULLIFIERS_PER_TX { + if (i as u32) < (MAX_nullifiers_PER_TEST as u32) { + sorted_nullifiers[i] = sorted_new_nullifier_tuples[i].elem; + sorted_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; + } else { + sorted_nullifiers[i] = 0; + sorted_nullifiers_indexes[i] = i; + } + } + + let mut pre_existing_nullifiers = self.pre_existing_nullifiers; + + for i in 0..MAX_nullifiers_PER_TEST { + if i < self.nullifiers.len() { + let sorted_tuple = sorted_new_nullifier_tuples[i]; + let new_nullifier = sorted_tuple.elem; + let original_index = sorted_tuple.original_index; + + let low_index = self.nullifiers.get_unchecked(original_index).existing_index; + + kernel_data.public_inputs.end.nullifiers[original_index] = new_nullifier; + + let mut low_preimage = pre_existing_nullifiers[low_index]; + nullifier_predecessor_preimages[i] = low_preimage; + low_nullifier_membership_witness[i] = MembershipWitness { + leaf_index: low_index as Field, + sibling_path: nullifier_tree.get_sibling_path(low_index), + }; + + low_preimage.next_nullifier = new_nullifier; + low_preimage.next_index = start_nullifier_tree_snapshot + .next_available_leaf_index as u32 + + original_index; + pre_existing_nullifiers[low_index] = low_preimage; + + nullifier_tree.update_leaf(low_index, low_preimage.hash()); + } + } + + ( + nullifier_predecessor_preimages, low_nullifier_membership_witness, + sorted_nullifiers, sorted_nullifiers_indexes, + ) + } + + unconstrained fn build_inputs(mut self) -> BaseRollupInputs { + let mut kernel_data = + KernelData { public_inputs: self.kernel_data.to_kernel_circuit_public_inputs() }; + + let start_note_hash_tree = NonEmptyMerkleTree::new( + self.pre_existing_notes, + [0; NOTE_HASH_TREE_HEIGHT], + [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], + [0; NOTE_HASH_SUBTREE_HEIGHT], + ); + let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_note_hash_tree.get_root(), + next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, + }; + let note_hash_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( + start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), + [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], + ); + + let mut start_nullifier_tree = NonEmptyMerkleTree::new( + self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], + [0; NULLIFIER_SUBTREE_HEIGHT], + ); + + let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_nullifier_tree.get_root(), + next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, + }; + + let mut start_public_data_tree = NonEmptyMerkleTree::new( + self.pre_existing_public_data.map(|preimage: PublicDataTreeLeafPreimage| { + preimage.hash() + }), + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT], + [0; PUBLIC_DATA_SUBTREE_HEIGHT], + ); + let start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { + root: start_public_data_tree.get_root(), + next_available_leaf_index: start_public_data_tree.get_next_available_index() as u32, + }; + + let fee_payer_fee_juice_balance_read_hint = + self.build_fee_payer_fee_juice_balance_read_hint(start_public_data_tree); + + let start_archive = NonEmptyMerkleTree::new( + self.pre_existing_blocks, + [0; ARCHIVE_HEIGHT], + [0; ARCHIVE_HEIGHT - 1], + [0; 1], + ); + self.constants.last_archive = AppendOnlyTreeSnapshot { + root: start_archive.get_root(), + next_available_leaf_index: start_archive.get_next_available_index() as u32, + }; + + let (nullifier_predecessor_preimages, nullifier_predecessor_membership_witnesses, sorted_nullifiers, sorted_nullifier_indexes) = self + .update_nullifier_tree_with_new_leaves( + &mut start_nullifier_tree, + &mut kernel_data, + start_nullifier_tree_snapshot, + ); + + let nullifier_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( + start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), + [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + ); + + let (public_data_sibling_path, sorted_public_data_writes, sorted_public_data_writes_indexes, low_public_data_writes_preimages, low_public_data_writes_witnesses, _new_subtree) = update_public_data_tree( + &mut start_public_data_tree, + &mut kernel_data, + start_public_data_tree_snapshot, + self.public_data_writes, + self.protocol_public_data_writes, + self.final_public_data_writes, + self.pre_existing_public_data, + ); + + let start = PartialStateReference { + note_hash_tree: start_note_hash_tree_snapshot, + nullifier_tree: start_nullifier_tree_snapshot, + public_data_tree: start_public_data_tree_snapshot, + }; + + let state_diff_hints = StateDiffHints { + nullifier_predecessor_preimages, + nullifier_predecessor_membership_witnesses, + sorted_nullifiers, + sorted_nullifier_indexes, + note_hash_subtree_sibling_path, + nullifier_subtree_sibling_path, + public_data_sibling_path, + }; + + BaseRollupInputs { + kernel_data, + start, + state_diff_hints, + sorted_public_data_writes, + sorted_public_data_writes_indexes, + low_public_data_writes_preimages, + low_public_data_writes_witnesses, + archive_root_membership_witness: MembershipWitness { + leaf_index: 0, + sibling_path: start_archive.get_sibling_path(0), + }, + constants: self.constants, + transaction_fee: self.transaction_fee, + fee_payer_fee_juice_balance_read_hint, + } + } + + fn execute(self) -> BaseOrMergeRollupPublicInputs { + let inputs = unsafe { self.build_inputs() }; + inputs.base_rollup_circuit() + } + + fn succeeds(self) { + let _ = self.execute(); + } + + fn fails(self) { + let _ = self.execute(); + } + } + + impl Empty for BaseRollupInputsBuilder { + fn empty() -> Self { + BaseRollupInputsBuilder { + kernel_data: FixtureBuilder::new(), + transaction_fee: 0, + pre_existing_notes: [0; MAX_NOTE_HASHES_PER_TX], + pre_existing_nullifiers: [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX], + pre_existing_contracts: [0; 2], + pre_existing_public_data: [ + PublicDataTreeLeafPreimage::empty(); + MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + ], + pre_existing_blocks: [0; 2], + public_data_writes: BoundedVec::new(), + protocol_public_data_writes: BoundedVec::new(), + final_public_data_writes: BoundedVec::new(), + nullifiers: BoundedVec::new(), + constants: ConstantRollupData::empty(), + fee_payer_fee_juice_balance_pre_existing_public_data_index: Option::none(), + } + } + } + + #[test] + unconstrained fn note_hashes_tree() { + let mut builder = BaseRollupInputsBuilder::new(); + + let note_hashes = [27, 28, 29, 30, 31, 32]; + for i in 0..note_hashes.len() { + builder.kernel_data.add_new_note_hash(note_hashes[i]); + } + let mut expected_commitments_tree = NonEmptyMerkleTree::new( + [0; MAX_NOTE_HASHES_PER_TX * 2], + [0; NOTE_HASH_TREE_HEIGHT], + [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], + [0; NOTE_HASH_SUBTREE_HEIGHT + 1], + ); + + let outputs = builder.execute(); + let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: expected_commitments_tree.get_root(), + next_available_leaf_index: MAX_NOTE_HASHES_PER_TX as u32, + }; + assert(outputs.start.note_hash_tree.eq(expected_start_note_hash_tree_snapshot)); + + for i in 0..note_hashes.len() { + expected_commitments_tree.update_leaf(i + MAX_NOTE_HASHES_PER_TX, note_hashes[i]); + } + let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { + root: expected_commitments_tree.get_root(), + next_available_leaf_index: (MAX_NOTE_HASHES_PER_TX * 2) as u32, + }; + assert(outputs.end.note_hash_tree.eq(expected_end_note_hash_tree_snapshot)); + } + + #[test] + unconstrained fn new_nullifier_tree_empty() { + // This test checks for insertions of all 0 values + // In this special case we will not need to provide sibling paths to check insertion of the nullifier values + // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left + // empty to begin with. + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.succeeds(); + } + + #[test] + unconstrained fn nullifier_insertion_test() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 0, value: 1 }); + let mut tree_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX * 2]; + tree_nullifiers[0] = NullifierLeafPreimage { + nullifier: 0, + next_nullifier: 1, + next_index: MAX_NULLIFIERS_PER_TX, + }; + tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; + tree_nullifiers[MAX_NULLIFIERS_PER_TX] = + NullifierLeafPreimage { nullifier: 1, next_nullifier: 7, next_index: 1 }; + + let mut end_nullifier_tree = NonEmptyMerkleTree::new( + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + [0; NULLIFIER_SUBTREE_HEIGHT + 1], + ); + + let output = builder.execute(); + + assert(output.end.nullifier_tree.eq( + AppendOnlyTreeSnapshot { + root: end_nullifier_tree.get_root(), + next_available_leaf_index: 2 * MAX_NULLIFIERS_PER_TX as u32, + }, + )); + } + + #[test] + unconstrained fn new_nullifier_tree_all_larger() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + for i in 1..builder.nullifiers.max_len() { + builder.nullifiers.push( + NullifierInsertion { existing_index: 1, value: (8 + i) as Field }, + ); + } + + let output = builder.execute(); + let mut tree_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX * 2]; + tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; + + tree_nullifiers[1] = NullifierLeafPreimage { + nullifier: 7, + next_nullifier: 8, + next_index: MAX_NULLIFIERS_PER_TX, + }; + + let last_index = builder.nullifiers.max_len() - 1; + for i in 0..last_index { + tree_nullifiers[MAX_NULLIFIERS_PER_TX + i] = NullifierLeafPreimage { + nullifier: (8 + i) as Field, + next_nullifier: (8 + i + 1) as Field, + next_index: MAX_NULLIFIERS_PER_TX + i + 1, + }; + } + tree_nullifiers[MAX_NULLIFIERS_PER_TX + last_index] = NullifierLeafPreimage { + nullifier: (8 + last_index) as Field, + next_nullifier: 0, + next_index: 0, + }; + + let mut end_nullifier_tree = NonEmptyMerkleTree::new( + tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), + [0; NULLIFIER_TREE_HEIGHT], + [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], + [0; NULLIFIER_SUBTREE_HEIGHT + 1], + ); + + assert(output.end.nullifier_tree.eq( + AppendOnlyTreeSnapshot { + root: end_nullifier_tree.get_root(), + next_available_leaf_index: 2 * MAX_NULLIFIERS_PER_TX as u32, + }, + )); + } + + #[test(should_fail_with = "Invalid low leaf")] + unconstrained fn new_nullifier_tree_double_spend() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + + builder.fails(); + } + + #[test(should_fail_with = "Invalid low leaf")] + unconstrained fn new_nullifier_tree_double_spend_same_batch() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_nullifiers[0] = + NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; + builder.pre_existing_nullifiers[1] = + NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; + + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); + + builder.fails(); + } + + #[test] + unconstrained fn empty_tx_effects_hash() { + let outputs = BaseRollupInputsBuilder::new().execute(); + + let hash_input_flattened = [0; TX_EFFECTS_HASH_INPUT_FIELDS * 32]; + let sha_digest = std::hash::sha256(hash_input_flattened); + let expected_tx_effects_hash = field_from_bytes_32_trunc(sha_digest); + assert_eq(outputs.txs_effects_hash, expected_tx_effects_hash); + } + + #[test] + unconstrained fn empty_block_out_hash() { + let outputs = BaseRollupInputsBuilder::new().execute(); + assert_eq(outputs.out_hash, 0); + } + + #[test] + unconstrained fn nonempty_block_out_hash() { + let mut builder = BaseRollupInputsBuilder::new(); + + for i in 0..MAX_L2_TO_L1_MSGS_PER_TX { + builder.kernel_data.add_exposed_l2_to_l1_message( + i as Field, + EthAddress::from_field(1 + i as Field), + ); + } + + let out_hash = builder.execute().out_hash; + let siloed_l2_to_l1_msgs = builder.kernel_data.l2_to_l1_msgs.map( + |l2_to_l1_message: ScopedL2ToL1Message| silo_l2_to_l1_message( + l2_to_l1_message, + builder.constants.global_variables.version, + builder.constants.global_variables.chain_id, + ), + ); + + // Since we fill the tree completely, we know to expect a full tree as below + let expected_tree = dep::types::merkle_tree::variable_merkle_tree::tests::generate_full_sha_tree( + siloed_l2_to_l1_msgs.storage(), + ); + assert_eq(out_hash, expected_tree.get_root()); + } + + #[test(should_fail_with = "membership check failed")] + unconstrained fn compute_membership_archive_negative() { + let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); + inputs.archive_root_membership_witness.sibling_path[0] = 27; + let _output = inputs.base_rollup_circuit(); + } + + #[test] + unconstrained fn constants_dont_change() { + let inputs = BaseRollupInputsBuilder::new().build_inputs(); + let outputs = inputs.base_rollup_circuit(); + + assert(inputs.constants.eq(outputs.constants)); + } + + #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] + unconstrained fn constants_dont_match_kernels_chain_id() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.chain_id = 3; + builder.fails(); + } + + #[test(should_fail_with = "kernel version does not match the rollup version")] + unconstrained fn constants_dont_match_kernels_version() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.version += 1; + builder.fails(); + } + + #[test(should_fail_with = "kernel global variables do not match the rollup global variables")] + unconstrained fn constants_global_variables_dont_match_kernels() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.kernel_data.global_variables.block_number = 6; + builder.constants.global_variables.block_number = 7; + builder.fails(); + } + + #[test(should_fail_with = "kernel max_block_number is smaller than block number")] + unconstrained fn constants_dont_satisfy_smaller_max_block_number() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.block_number = 42; + builder.kernel_data.set_max_block_number(5); + builder.fails(); + } + + #[test] + unconstrained fn constants_satisfy_equal_max_block_number() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.block_number = 42; + builder.kernel_data.set_max_block_number(42); + builder.succeeds(); + } + + #[test] + unconstrained fn constants_satisfy_larger_max_block_number() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.constants.global_variables.block_number = 42; + builder.kernel_data.set_max_block_number(4294967295); + builder.succeeds(); + } + + #[test] + unconstrained fn num_txs_is_1() { + let outputs = BaseRollupInputsBuilder::new().execute(); + + assert_eq(outputs.num_txs, 1); + } + + #[test] + unconstrained fn single_public_state_write() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_public_data[0] = + PublicDataTreeLeafPreimage { slot: 27, value: 28, next_slot: 0, next_index: 0 }; + builder.public_data_writes.push((0, PublicDataTreeLeaf { slot: 27, value: 29 })); + let outputs = builder.execute(); + + let updated_leaf = + PublicDataTreeLeafPreimage { slot: 27, value: 29, next_slot: 0, next_index: 0 }; + + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + [updated_leaf.hash(), 0], + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - 1], + [0; 1], + ); + + assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); + } + + #[test] + unconstrained fn multiple_public_state_read_writes() { + let mut builder = BaseRollupInputsBuilder::new(); + + builder.pre_existing_public_data[0] = + PublicDataTreeLeafPreimage { slot: 20, value: 40, next_slot: 28, next_index: 1 }; + builder.pre_existing_public_data[1] = + PublicDataTreeLeafPreimage { slot: 28, value: 41, next_slot: 29, next_index: 2 }; + builder.pre_existing_public_data[2] = + PublicDataTreeLeafPreimage { slot: 29, value: 42, next_slot: 30, next_index: 3 }; + builder.pre_existing_public_data[3] = + PublicDataTreeLeafPreimage { slot: 30, value: 43, next_slot: 0, next_index: 0 }; + builder.public_data_writes.push((0, PublicDataTreeLeaf { slot: 25, value: 60 })); + builder.public_data_writes.push((0, PublicDataTreeLeaf { slot: 20, value: 90 })); + + let outputs = builder.execute(); + + let mut public_data_leaves = [0; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2]; + public_data_leaves[0] = PublicDataTreeLeafPreimage { + slot: 20, + value: 90, + next_slot: 25, + next_index: MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + } + .hash(); + public_data_leaves[1] = + PublicDataTreeLeafPreimage { slot: 28, value: 41, next_slot: 29, next_index: 2 }.hash(); + public_data_leaves[2] = + PublicDataTreeLeafPreimage { slot: 29, value: 42, next_slot: 30, next_index: 3 }.hash(); + public_data_leaves[3] = + PublicDataTreeLeafPreimage { slot: 30, value: 43, next_slot: 0, next_index: 0 }.hash(); + public_data_leaves[MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = + PublicDataTreeLeafPreimage { slot: 25, value: 60, next_slot: 28, next_index: 1 }.hash(); + + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + public_data_leaves, + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT - 1], + [0; PUBLIC_DATA_SUBTREE_HEIGHT + 1], + ); + + assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); + } + + #[test] + unconstrained fn updates_fee_payer_balance_with_new_data_write() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + let initial_balance = 300_000; + let tx_fee = 100_000; + let expected_balance = 200_000; + + let mut builder = BaseRollupInputsBuilder::new(); + + // Set fee payer + builder.kernel_data.fee_payer = fee_payer; + + // Set pre-existing balance + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); + + // Set values for computing exact tx_fee + builder.transaction_fee = tx_fee; + + // Set expected protocol data update + builder.protocol_public_data_writes.push(( + 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, + )); + + let outputs = builder.execute(); + + // The new public data tree should have updated the balance of the fee payer + let updated_leaf = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: expected_balance, + next_slot: 0, + next_index: 0, + }; + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + [updated_leaf.hash(), 0], + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - 1], + [0; 1], + ); + + assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); + } + + #[test] + unconstrained fn updates_fee_payer_balance_in_existing_data_write() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + let initial_balance = 100_000; + let after_claim_balance = 300_000; + let tx_fee = 100_000; + let expected_balance = 200_000; + + let mut builder = BaseRollupInputsBuilder::new(); + + // Set fee payer + builder.kernel_data.fee_payer = fee_payer; + + // Set pre-existing balance, but set no hint for it since we'll update a user update request + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + + // Set values for computing exact tx_fee + builder.transaction_fee = tx_fee; + + // Create an existing data update that corresponds to a claim + builder.public_data_writes.push(( + 0, PublicDataTreeLeaf { slot: balance_slot, value: after_claim_balance }, + )); + + // Set expected data updates after base rollup runs + // Note that we tweak the final_public_data_writes directly, since we need to overwrite the output of the user public_data_writes + builder.final_public_data_writes.push(( + 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, + )); + + let outputs = builder.execute(); + + // The new public data tree should have updated the balance of the fee payer + let updated_leaf = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: expected_balance, + next_slot: 0, + next_index: 0, + }; + let mut expected_public_data_tree = NonEmptyMerkleTree::new( + [updated_leaf.hash(), 0], + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - 1], + [0; 1], + ); + + assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); + } + + #[test(should_fail_with = "Not enough balance for fee payer to pay for transaction")] + unconstrained fn fails_to_update_fee_payer_balance_if_not_enough_funds() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + // Set low initial balance so it fails! + let initial_balance = 10_000; + let tx_fee = 100_000; + + let mut builder = BaseRollupInputsBuilder::new(); + + // Set fee payer + builder.kernel_data.fee_payer = fee_payer; + + // Set pre-existing balance + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); + + // Set values for computing exact tx_fee + builder.transaction_fee = tx_fee; + + // Set expected protocol data update + builder.protocol_public_data_writes.push(( + 0, PublicDataTreeLeaf { slot: balance_slot, value: -90_000 }, + )); + + builder.fails(); + } + + #[test(should_fail_with = "Wrong leaf slot for Fee Juice balance read hint")] + unconstrained fn fails_to_update_fee_payer_balance_if_wrong_read_hint() { + let fee_payer = AztecAddress::from_field(0x1234); + let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); + let initial_balance = 300_000; + let expected_balance = 200_000; + let tx_fee = 100_000; + + let mut builder = BaseRollupInputsBuilder::new(); + + // Set fee payer + builder.kernel_data.fee_payer = fee_payer; + + // Set pre-existing balance in index 0 + builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { + slot: balance_slot, + value: initial_balance, + next_slot: 0, + next_index: 0, + }; + + builder.pre_existing_public_data[1] = PublicDataTreeLeafPreimage { + slot: 1, + value: initial_balance, + next_slot: balance_slot, + next_index: 0, + }; + + // But point the read hint to the wrong one! + builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(1); + + // Set values for computing exact tx_fee + builder.transaction_fee = tx_fee; + + // Set expected protocol data update + builder.protocol_public_data_writes.push(( + 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, + )); + + builder.fails(); + } + + #[test] + fn valid_previous_kernel_empty() { + let builder = unsafe { + BaseRollupInputsBuilder::new_with_previous_kernel(PRIVATE_KERNEL_EMPTY_INDEX) + }; + + let _res = builder.execute(); + } +} From 0649da64118bcd50bdcee04879cf796f5e88d4bc Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 18 Nov 2024 12:34:15 +0000 Subject: [PATCH 10/25] restore public base tests --- .../rollup-lib/src/base/base_rollup_inputs.nr | 1466 ----------------- .../src/base/components/avm_proof_data.nr | 17 - .../rollup-lib/src/base/components/mod.nr | 1 - .../src/base/private_base_rollup.nr | 17 +- .../rollup-lib/src/base/public_base_rollup.nr | 308 ++-- .../rollup-lib/src/base/state_diff_hints.nr | 40 +- .../{avm_circuit_public_inputs.nr => avm.nr} | 19 +- .../crates/types/src/abis/mod.nr | 2 +- .../crates/types/src/tests/fixture_builder.nr | 27 +- 9 files changed, 272 insertions(+), 1625 deletions(-) delete mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/avm_proof_data.nr rename noir-projects/noir-protocol-circuits/crates/types/src/abis/{avm_circuit_public_inputs.nr => avm.nr} (94%) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr deleted file mode 100644 index 28bae2e2bd9..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ /dev/null @@ -1,1466 +0,0 @@ -use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, - constant_rollup_data::ConstantRollupData, - }, - base::state_diff_hints::StateDiffHints, - components::{compute_kernel_out_hash, compute_tx_effects_hash}, -}; -use dep::types::{ - abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, - kernel_circuit_public_inputs::KernelCircuitPublicInputs, - nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, - }, - address::AztecAddress, - constants::{ - ARCHIVE_HEIGHT, FEE_JUICE_ADDRESS, MAX_NOTE_HASHES_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - NOTE_HASH_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, - PRIVATE_KERNEL_EMPTY_INDEX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, - }, - data::{ - hash::{compute_public_data_tree_index, compute_public_data_tree_value}, - public_data_hint::PublicDataHint, - PublicDataTreeLeaf, - PublicDataTreeLeafPreimage, - }, - hash::silo_l2_to_l1_message, - merkle_tree::{ - append_only_tree, assert_check_membership, calculate_empty_tree_root, - calculate_subtree_root, indexed_tree, MembershipWitness, - }, - messaging::l2_to_l1_message::ScopedL2ToL1Message, - partial_state_reference::PartialStateReference, - storage::map::derive_storage_slot_in_map, - traits::is_empty, - utils::{arrays::find_index_hint, field::{full_field_greater_than, full_field_less_than}}, -}; - -// Temporary struct to avoid changing all the references to kernel_data. Will remove it and just pass in the public_inputs. -struct KernelData { - public_inputs: KernelCircuitPublicInputs, -} - -pub struct BaseRollupInputs { - kernel_data: KernelData, - start: PartialStateReference, - - state_diff_hints: StateDiffHints, - transaction_fee: Field, - fee_payer_fee_juice_balance_read_hint: PublicDataHint, - - // TODO: The following 6 values are eventually going to be nuked from here. See discussion: - // https://aztecprotocol.slack.com/archives/C060PU5R327/p1701965354071269 - sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - - archive_root_membership_witness: MembershipWitness, - - constants: ConstantRollupData, -} - -impl BaseRollupInputs { - pub fn base_rollup_circuit(self) -> BaseOrMergeRollupPublicInputs { - // Verify the kernel chain_id and versions - assert( - self.kernel_data.public_inputs.constants.tx_context.chain_id - == self.constants.global_variables.chain_id, - "kernel chain_id does not match the rollup chain_id", - ); - assert( - self.kernel_data.public_inputs.constants.tx_context.version - == self.constants.global_variables.version, - "kernel version does not match the rollup version", - ); - assert( - self.kernel_data.public_inputs.constants.vk_tree_root == self.constants.vk_tree_root, - "kernel vk_tree_root does not match the rollup vk_tree_root", - ); - assert( - self.kernel_data.public_inputs.constants.protocol_contract_tree_root - == self.constants.protocol_contract_tree_root, - "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", - ); - - // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. - // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? - assert( - self.kernel_data.public_inputs.constants.global_variables.is_empty() - | ( - self.kernel_data.public_inputs.constants.global_variables - == self.constants.global_variables - ), - "kernel global variables do not match the rollup global variables", - ); - - self.validate_kernel_start_state(); - - let rollup_validation_requests = self.kernel_data.public_inputs.rollup_validation_requests; - - // Verify the max block number - // TODO #5345: why is block_number a Field and not u32? - if rollup_validation_requests.max_block_number.is_some() { - assert( - self.constants.global_variables.block_number as u32 - <= rollup_validation_requests.max_block_number.unwrap_unchecked(), - "kernel max_block_number is smaller than block number", - ); - } - - let commitments_tree_subroot = self.calculate_commitments_subtree(); - - let empty_commitments_subtree_root = calculate_empty_tree_root(NOTE_HASH_SUBTREE_HEIGHT); - - let end_note_hash_tree_snapshot = append_only_tree::insert_subtree_to_snapshot_tree( - self.start.note_hash_tree, - self.state_diff_hints.note_hash_subtree_sibling_path, - empty_commitments_subtree_root, - commitments_tree_subroot, - NOTE_HASH_SUBTREE_HEIGHT as u8, - ); - - // Insert nullifiers: - let end_nullifier_tree_snapshot = - self.check_nullifier_tree_non_membership_and_insert_to_tree(); - - // Inject protocol update requests for deducting tx_fee from fee_payer's balance - let all_public_data_update_requests = - self.calculate_all_public_data_update_requests(self.transaction_fee); - - // Validate public data update requests and update public data tree - let end_public_data_tree_snapshot = - self.validate_and_process_public_state(all_public_data_update_requests); - - // Calculate the tx effects hash of the transaction - let siloed_l2_to_l1_msgs = self.kernel_data.public_inputs.end.l2_to_l1_msgs.map( - |message: ScopedL2ToL1Message| silo_l2_to_l1_message( - message, - self.kernel_data.public_inputs.constants.tx_context.version, - self.kernel_data.public_inputs.constants.tx_context.chain_id, - ), - ); - let out_hash = compute_kernel_out_hash(siloed_l2_to_l1_msgs); - let tx_effects_hash = compute_tx_effects_hash( - self.kernel_data.public_inputs.end, - self.kernel_data.public_inputs.revert_code, - self.transaction_fee, - all_public_data_update_requests, - out_hash, - ); - - // Perform membership checks that the notes provided exist within the historical trees data - self.perform_archive_membership_checks(); - - BaseOrMergeRollupPublicInputs { - rollup_type: BASE_ROLLUP_TYPE, - num_txs: 1, - constants: self.constants, - start: self.start, - end: PartialStateReference { - note_hash_tree: end_note_hash_tree_snapshot, - nullifier_tree: end_nullifier_tree_snapshot, - public_data_tree: end_public_data_tree_snapshot, - }, - txs_effects_hash: tx_effects_hash, - out_hash, - accumulated_fees: self.transaction_fee, - } - } - - // TODO(Kev): This should say calculate_commitments_subtree_root - // Cpp code says calculate_commitments_subtree, so I'm leaving it as is for now - fn calculate_commitments_subtree(self) -> Field { - calculate_subtree_root(self.kernel_data.public_inputs.end.note_hashes) - } - - fn check_nullifier_tree_non_membership_and_insert_to_tree(self) -> AppendOnlyTreeSnapshot { - indexed_tree::batch_insert( - self.start.nullifier_tree, - self.kernel_data.public_inputs.end.nullifiers, - self.state_diff_hints.sorted_nullifiers, - self.state_diff_hints.sorted_nullifier_indexes, - self.state_diff_hints.nullifier_subtree_sibling_path, - self.state_diff_hints.nullifier_predecessor_preimages, - self.state_diff_hints.nullifier_predecessor_membership_witnesses.map( - |witness: MembershipWitness| { - MembershipWitness { - leaf_index: witness.leaf_index, - sibling_path: witness.sibling_path, - } - }, - ), - |low_leaf: NullifierLeafPreimage, nullifier: Field| { - // Is valid low leaf - let is_less_than_nullifier = full_field_less_than(low_leaf.nullifier, nullifier); - let is_next_greater_than = full_field_less_than(nullifier, low_leaf.next_nullifier); - - (!low_leaf.is_empty()) - & is_less_than_nullifier - & ( - is_next_greater_than - | ((low_leaf.next_index == 0) & (low_leaf.next_nullifier == 0)) - ) - }, - |low_leaf: NullifierLeafPreimage, nullifier: Field, nullifier_index: u32| { - // Update low leaf - NullifierLeafPreimage { - nullifier: low_leaf.nullifier, - next_nullifier: nullifier, - next_index: nullifier_index, - } - }, - |nullifier: Field, low_leaf: NullifierLeafPreimage| { - // Build insertion leaf - NullifierLeafPreimage { - nullifier: nullifier, - next_nullifier: low_leaf.next_nullifier, - next_index: low_leaf.next_index, - } - }, - [0; NULLIFIER_SUBTREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT], - ) - } - - fn create_nullifier_subtree(leaves: [NullifierLeafPreimage; N]) -> Field { - calculate_subtree_root(leaves.map(|leaf: NullifierLeafPreimage| leaf.hash())) - } - - fn validate_kernel_start_state(self) { - let kernel_state = self.kernel_data.public_inputs.start_state; - if !is_empty(kernel_state) { - assert( - kernel_state.note_hash_tree.eq(self.start.note_hash_tree), - "Mismatch start state for note hash tree", - ); - assert( - kernel_state.nullifier_tree.eq(self.start.nullifier_tree), - "Mismatch start state for nullifier tree", - ); - assert( - kernel_state.public_data_tree.eq(self.start.public_data_tree), - "Mismatch start state for public data tree", - ); - } - } - - fn validate_and_process_public_state( - self, - all_update_requests: [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - ) -> AppendOnlyTreeSnapshot { - let end_public_data_tree_snapshot = insert_public_data_update_requests( - self.start.public_data_tree, - all_update_requests.map(|w: PublicDataWrite| { - PublicDataTreeLeaf { slot: w.leaf_slot, value: w.value } - }), - self.sorted_public_data_writes, - self.sorted_public_data_writes_indexes, - self.low_public_data_writes_preimages, - self.low_public_data_writes_witnesses, - self.state_diff_hints.public_data_sibling_path, - ); - - end_public_data_tree_snapshot - } - - // Returns an array with all public data update requests for this tx. This includes all update requests - // generated by app circuits, plus the protocol update requests injected by this circuit. The only protocol - // update request we have at the time of this writing is deducting the tx_fee from the fee_payer balance. - fn calculate_all_public_data_update_requests( - self, - tx_fee: Field, - ) -> [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] { - let mut all_update_requests: [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = - [PublicDataWrite::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - all_update_requests[i] = self.kernel_data.public_inputs.end.public_data_writes[i]; - } - - let (payment_update_request, payment_update_index) = - self.build_or_patch_payment_update_request(tx_fee); - all_update_requests[payment_update_index] = payment_update_request; - - all_update_requests - } - - // Deducts the tx_fee from the FeeJuice balance of the fee_payer. If there is already a PublicDataUpdateRequest - // in this tx for their balance (because they issued a 'claim' to increase their balance by bridging from L1), - // update it by subtracting the tx_fee. Otherwise, build a new PublicDataUpdateRequest to subtract the tx_fee - // from the balance of the fee_payer, using the fee_payer_fee_juice_balance_read_hint to read the current balance. - // Returns the data update request that subtracts the tx_fee from the fee_payer's balance, and the index where it - // should be inserted in the public data update requests array. - fn build_or_patch_payment_update_request(self, tx_fee: Field) -> (PublicDataWrite, u32) { - let fee_payer = self.kernel_data.public_inputs.fee_payer; - - // TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx - if !fee_payer.is_zero() { - let read_hint = self.fee_payer_fee_juice_balance_read_hint; - let leaf_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - let existing_update_index = unsafe { - find_index_hint( - self.kernel_data.public_inputs.end.public_data_writes, - |w: PublicDataWrite| w.leaf_slot == leaf_slot, - ) - }; - - if existing_update_index != MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - // Is there a balance update already in this tx? If so, update it and return its index. - let existing_update = - self.kernel_data.public_inputs.end.public_data_writes[existing_update_index]; - assert( - existing_update.leaf_slot == leaf_slot, - "Wrong leaf slot for Fee Juice balance update request", - ); - assert( - !existing_update.value.lt(tx_fee), - "Not enough balance for fee payer after claim to pay for transaction", - ); - - let value = compute_public_data_tree_value(existing_update.value - tx_fee); - let protocol_update_request = PublicDataWrite { leaf_slot, value }; - (protocol_update_request, existing_update_index as u32) - } else { - // Otherwise, build a new one to be inserted into the protocol update requests at the end of the array. - read_hint.validate(self.start.public_data_tree.root); - - let balance = read_hint.value; - assert( - read_hint.leaf_slot == leaf_slot, - "Wrong leaf slot for Fee Juice balance read hint", - ); - assert( - !balance.lt(tx_fee), - "Not enough balance for fee payer to pay for transaction", - ); - - let value = compute_public_data_tree_value(balance - tx_fee); - let protocol_update_request = PublicDataWrite { leaf_slot, value }; - (protocol_update_request, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) - } - } else { - // Nothing to do, just place an empty update request at the end of the array - (PublicDataWrite::empty(), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) - } - } - - // Check that the block header used by each kernel is a member of the blocks tree --> since the block header - // contains roots of all the trees this is sufficient to verify that the tree roots used by kernels are correct - fn perform_archive_membership_checks(self) { - // For each of the block header (their block hashes), we need to do an inclusion proof - // against the blocks tree root from the beginning of a rollup provided in the rollup constants - let archive_root = self.constants.last_archive.root; - - // Rebuild the block hash - let header = self.kernel_data.public_inputs.constants.historical_header; - let previous_block_hash = header.hash(); - - let previous_block_hash_witness = self.archive_root_membership_witness; - - // Now check that the previous block hash is in the blocks tree from the beginning of the rollup - assert_check_membership( - previous_block_hash, - previous_block_hash_witness.leaf_index, - previous_block_hash_witness.sibling_path, - archive_root, - ); - } -} - -fn insert_public_data_update_requests( - prev_snapshot: AppendOnlyTreeSnapshot, - public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes: [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - sorted_public_data_writes_indexes: [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_writes_subtree_sibling_path: [Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], -) -> AppendOnlyTreeSnapshot { - indexed_tree::batch_insert( - prev_snapshot, - public_data_writes, - sorted_public_data_writes, - sorted_public_data_writes_indexes, - public_data_writes_subtree_sibling_path, - low_public_data_writes_preimages, - low_public_data_writes_witnesses.map( - |witness: MembershipWitness| { - MembershipWitness { - leaf_index: witness.leaf_index, - sibling_path: witness.sibling_path, - } - }, - ), - |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf| { - // Is valid low preimage - let is_update = low_preimage.slot == write.slot; - let is_low_empty = low_preimage.is_empty(); - - let is_less_than_slot = full_field_less_than(low_preimage.slot, write.slot); - let is_next_greater_than = full_field_less_than(write.slot, low_preimage.next_slot); - let is_in_range = is_less_than_slot - & ( - is_next_greater_than - | ((low_preimage.next_index == 0) & (low_preimage.next_slot == 0)) - ); - - (!is_low_empty) & (is_update | is_in_range) - }, - |low_preimage: PublicDataTreeLeafPreimage, write: PublicDataTreeLeaf, write_index: u32| { - // Update low leaf - let is_update = low_preimage.slot == write.slot; - if is_update { - PublicDataTreeLeafPreimage { - slot: low_preimage.slot, - value: write.value, - next_slot: low_preimage.next_slot, - next_index: low_preimage.next_index, - } - } else { - PublicDataTreeLeafPreimage { - slot: low_preimage.slot, - value: low_preimage.value, - next_slot: write.slot, - next_index: write_index, - } - } - }, - |write: PublicDataTreeLeaf, low_preimage: PublicDataTreeLeafPreimage| { - // Build insertion leaf - let is_update = low_preimage.slot == write.slot; - if is_update { - PublicDataTreeLeafPreimage::empty() - } else { - PublicDataTreeLeafPreimage { - slot: write.slot, - value: write.value, - next_slot: low_preimage.next_slot, - next_index: low_preimage.next_index, - } - } - }, - [0; PUBLIC_DATA_SUBTREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT], - ) -} - -fn compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer: AztecAddress) -> Field { - let balances_slot_in_fee_juice_contract = 1; - let fee_payer_balance_slot_in_fee_juice_contract = - derive_storage_slot_in_map(balances_slot_in_fee_juice_contract, fee_payer); - compute_public_data_tree_index( - FEE_JUICE_ADDRESS, - fee_payer_balance_slot_in_fee_juice_contract, - ) -} - -#[test] -fn consistent_not_hash_subtree_width() { - assert_eq( - MAX_NOTE_HASHES_PER_TX as Field, - 2.pow_32(NOTE_HASH_SUBTREE_HEIGHT as Field), - "note hash subtree width is incorrect", - ); -} - -#[test] -fn test_u256_less_than() { - assert(full_field_less_than(1, 1000)); - assert(!full_field_less_than(1000, 1000)); - assert(!full_field_less_than(1000, 1)); - assert(full_field_less_than(0, 0 - 1)); - assert(!full_field_less_than(0 - 1, 0)); -} - -#[test] -fn test_u256_greater_than() { - assert(full_field_greater_than(1000, 1)); - assert(!full_field_greater_than(1000, 1000)); - assert(!full_field_greater_than(1, 1000)); - assert(!full_field_greater_than(0, 0 - 1)); - assert(full_field_greater_than(0 - 1, 0)); -} - -mod tests { - use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, - constant_rollup_data::ConstantRollupData, - }, - base::{ - base_rollup_inputs::{ - BaseRollupInputs, compute_fee_payer_fee_juice_balance_leaf_slot, KernelData, - }, - state_diff_hints::StateDiffHints, - }, - components::TX_EFFECTS_HASH_INPUT_FIELDS, - }; - use dep::types::{ - abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, - nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, - }, - address::{AztecAddress, EthAddress}, - constants::{ - ARCHIVE_HEIGHT, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - NOTE_HASH_SUBTREE_HEIGHT, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NOTE_HASH_TREE_HEIGHT, - NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, - PRIVATE_KERNEL_EMPTY_INDEX, PRIVATE_KERNEL_TAIL_INDEX, - PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, - }, - data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, - hash::silo_l2_to_l1_message, - merkle_tree::MembershipWitness, - messaging::l2_to_l1_message::ScopedL2ToL1Message, - partial_state_reference::PartialStateReference, - tests::{fixture_builder::FixtureBuilder, fixtures, merkle_tree_utils::NonEmptyMerkleTree}, - traits::Empty, - utils::{ - arrays::get_sorted_tuple::get_sorted_tuple, - field::{field_from_bytes_32_trunc, full_field_less_than}, - }, - }; - - struct NullifierInsertion { - existing_index: u32, - value: Field, - } - - global MAX_nullifiers_PER_TEST: u32 = 4; - global MAX_PUBLIC_DATA_READS_PER_TEST: u32 = 2; - - fn update_public_data_tree( - public_data_tree: &mut NonEmptyMerkleTree, - kernel_data: &mut KernelData, - snapshot: AppendOnlyTreeSnapshot, - user_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - protocol_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - mut final_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - mut pre_existing_public_data: [PublicDataTreeLeafPreimage; EXISTING_LEAVES], - ) -> ([Field; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], [PublicDataTreeLeaf; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [PublicDataTreeLeafPreimage; EXISTING_LEAVES]) { - let mut subtree_path = [0; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH]; - let mut sorted_public_data_writes = - [PublicDataTreeLeaf::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - let mut sorted_public_data_writes_indexes = - [0 as u32; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - let mut low_public_data_writes_preimages = - [PublicDataTreeLeafPreimage::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - let mut low_public_data_writes_witnesses = - [MembershipWitness::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - let mut new_subtree = - [PublicDataTreeLeafPreimage::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; - - for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - if i < (user_public_data_writes.len()) { - let leaf = user_public_data_writes.get_unchecked(i).1; - - kernel_data.public_inputs.end.public_data_writes[i] = - PublicDataWrite { leaf_slot: leaf.slot, value: leaf.value }; - } - } - - // Default final_public_data_writes to user_public_data_writes + protocol_public_data_writes if not set - if final_public_data_writes.len() == 0 { - final_public_data_writes.extend_from_array(user_public_data_writes.storage); - final_public_data_writes.extend_from_array(protocol_public_data_writes.storage); - } - - let mut sorted_write_tuples = unsafe { - get_sorted_tuple( - final_public_data_writes.storage(), - |(_, leaf_a): (u32, PublicDataTreeLeaf), (_, leaf_b): (u32, PublicDataTreeLeaf)| { - full_field_less_than(leaf_b.slot, leaf_a.slot) - }, - ) - }; - - for i in 0..MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - if i < final_public_data_writes.len() { - let (low_leaf_index, leaf): (u32, PublicDataTreeLeaf) = sorted_write_tuples[i].elem; - - sorted_public_data_writes[i] = leaf; - sorted_public_data_writes_indexes[i] = sorted_write_tuples[i].original_index; - - if !leaf.is_empty() { - let low_leaf = pre_existing_public_data[low_leaf_index]; - if low_leaf.slot == leaf.slot { - pre_existing_public_data[low_leaf_index].value = leaf.value; - } else { - new_subtree[sorted_write_tuples[i].original_index] = PublicDataTreeLeafPreimage { - slot: leaf.slot, - value: leaf.value, - next_slot: low_leaf.next_slot, - next_index: low_leaf.next_index, - }; - pre_existing_public_data[low_leaf_index] = PublicDataTreeLeafPreimage { - slot: low_leaf.slot, - value: low_leaf.value, - next_slot: leaf.slot, - next_index: MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + i, - }; - } - low_public_data_writes_preimages[i] = low_leaf; - low_public_data_writes_witnesses[i] = MembershipWitness { - leaf_index: low_leaf_index as Field, - sibling_path: public_data_tree.get_sibling_path(low_leaf_index), - }; - - public_data_tree.update_leaf( - low_leaf_index, - pre_existing_public_data[low_leaf_index].hash(), - ); - } - } else { - sorted_public_data_writes[i] = PublicDataTreeLeaf::empty(); - sorted_public_data_writes_indexes[i] = i; - } - } - - subtree_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( - public_data_tree.get_sibling_path(snapshot.next_available_leaf_index as u32), - [0; PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH], - ); - - ( - subtree_path, sorted_public_data_writes, sorted_public_data_writes_indexes, - low_public_data_writes_preimages, low_public_data_writes_witnesses, - pre_existing_public_data, - ) - } - - struct BaseRollupInputsBuilder { - kernel_data: FixtureBuilder, - transaction_fee: Field, - pre_existing_notes: [Field; MAX_NOTE_HASHES_PER_TX], - pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], - pre_existing_contracts: [Field; 2], - pre_existing_public_data: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - pre_existing_blocks: [Field; 2], - public_data_reads: BoundedVec, - // Public data writes as returned from the last public kernel (ie all data writes generated by app code) - public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - // New public data writes to be created by the protocol (eg a data update request for updating fee payer balance) - protocol_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - // Public data writes after processing by the base rollup circuit (defaults to public_data_writes ++ protocol_public_data_writes if empty) - final_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - nullifiers: BoundedVec, - constants: ConstantRollupData, - // Index of the item in the pre_existing_public_data array that contains the fee payer's Fee Juice balance. - // Used for building the public data hint read for the payment update request. If set to none, no hint is built. - fee_payer_fee_juice_balance_pre_existing_public_data_index: Option, - } - - impl BaseRollupInputsBuilder { - fn new() -> Self { - let mut inputs = BaseRollupInputsBuilder::empty(); - inputs.kernel_data = FixtureBuilder::new().in_vk_tree(PRIVATE_KERNEL_TAIL_INDEX); - inputs.constants.global_variables.chain_id = fixtures::CHAIN_ID; - inputs.constants.global_variables.version = fixtures::VERSION; - inputs.constants.vk_tree_root = inputs.kernel_data.vk_tree_root; - - inputs.pre_existing_blocks[0] = inputs.kernel_data.historical_header.hash(); - - inputs - } - - unconstrained fn new_with_previous_kernel(previous_vk_index: u32) -> Self { - let mut builder = BaseRollupInputsBuilder::new(); - builder.kernel_data = builder.kernel_data.in_vk_tree(previous_vk_index); - builder - } - - fn build_fee_payer_fee_juice_balance_read_hint( - self, - start_public_data_tree: NonEmptyMerkleTree, - ) -> PublicDataHint { - self.fee_payer_fee_juice_balance_pre_existing_public_data_index.map_or( - PublicDataHint::empty(), - |leaf_index_u32: u32| { - let leaf_index = leaf_index_u32 as Field; - let leaf_preimage = self.pre_existing_public_data[leaf_index]; - let membership_witness = MembershipWitness { - leaf_index, - sibling_path: start_public_data_tree.get_sibling_path(leaf_index_u32), - }; - PublicDataHint { - leaf_slot: leaf_preimage.slot, - value: leaf_preimage.value, - override_counter: 0, - membership_witness, - leaf_preimage, - } - }, - ) - } - - fn extract_subtree_sibling_path( - path: [Field; FULL_HEIGHT], - mut sibling_path: [Field; SIBLING_PATH_LENGTH], - ) -> [Field; SIBLING_PATH_LENGTH] { - let subtree_height = FULL_HEIGHT - SIBLING_PATH_LENGTH; - for i in subtree_height..FULL_HEIGHT { - sibling_path[i - subtree_height] = path[i]; - } - sibling_path - } - - fn update_nullifier_tree_with_new_leaves( - mut self, - nullifier_tree: &mut NonEmptyMerkleTree, - kernel_data: &mut KernelData, - start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot, - ) -> ([NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], [MembershipWitness; MAX_NULLIFIERS_PER_TX], [Field; MAX_NULLIFIERS_PER_TX], [u32; MAX_NULLIFIERS_PER_TX]) { - let mut nullifier_predecessor_preimages = - [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX]; - let mut low_nullifier_membership_witness = - [MembershipWitness::empty(); MAX_NULLIFIERS_PER_TX]; - - let sorted_new_nullifier_tuples = unsafe { - get_sorted_tuple( - self.nullifiers.storage.map(|insertion: NullifierInsertion| insertion.value), - |a, b| full_field_less_than(b, a), - ) - }; - - let mut sorted_nullifiers = [0; MAX_NULLIFIERS_PER_TX]; - let mut sorted_nullifiers_indexes = [0; MAX_NULLIFIERS_PER_TX]; - - for i in 0..MAX_NULLIFIERS_PER_TX { - if (i as u32) < (MAX_nullifiers_PER_TEST as u32) { - sorted_nullifiers[i] = sorted_new_nullifier_tuples[i].elem; - sorted_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; - } else { - sorted_nullifiers[i] = 0; - sorted_nullifiers_indexes[i] = i; - } - } - - let mut pre_existing_nullifiers = self.pre_existing_nullifiers; - - for i in 0..MAX_nullifiers_PER_TEST { - if i < self.nullifiers.len() { - let sorted_tuple = sorted_new_nullifier_tuples[i]; - let new_nullifier = sorted_tuple.elem; - let original_index = sorted_tuple.original_index; - - let low_index = self.nullifiers.get_unchecked(original_index).existing_index; - - kernel_data.public_inputs.end.nullifiers[original_index] = new_nullifier; - - let mut low_preimage = pre_existing_nullifiers[low_index]; - nullifier_predecessor_preimages[i] = low_preimage; - low_nullifier_membership_witness[i] = MembershipWitness { - leaf_index: low_index as Field, - sibling_path: nullifier_tree.get_sibling_path(low_index), - }; - - low_preimage.next_nullifier = new_nullifier; - low_preimage.next_index = start_nullifier_tree_snapshot - .next_available_leaf_index as u32 - + original_index; - pre_existing_nullifiers[low_index] = low_preimage; - - nullifier_tree.update_leaf(low_index, low_preimage.hash()); - } - } - - ( - nullifier_predecessor_preimages, low_nullifier_membership_witness, - sorted_nullifiers, sorted_nullifiers_indexes, - ) - } - - unconstrained fn build_inputs(mut self) -> BaseRollupInputs { - let mut kernel_data = - KernelData { public_inputs: self.kernel_data.to_kernel_circuit_public_inputs() }; - - let start_note_hash_tree = NonEmptyMerkleTree::new( - self.pre_existing_notes, - [0; NOTE_HASH_TREE_HEIGHT], - [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT], - [0; NOTE_HASH_SUBTREE_HEIGHT], - ); - let start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_note_hash_tree.get_root(), - next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, - }; - let note_hash_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( - start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), - [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], - ); - - let mut start_nullifier_tree = NonEmptyMerkleTree::new( - self.pre_existing_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT], - [0; NULLIFIER_SUBTREE_HEIGHT], - ); - - let start_nullifier_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_nullifier_tree.get_root(), - next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, - }; - - let mut start_public_data_tree = NonEmptyMerkleTree::new( - self.pre_existing_public_data.map(|preimage: PublicDataTreeLeafPreimage| { - preimage.hash() - }), - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT], - [0; PUBLIC_DATA_SUBTREE_HEIGHT], - ); - let start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { - root: start_public_data_tree.get_root(), - next_available_leaf_index: start_public_data_tree.get_next_available_index() as u32, - }; - - let fee_payer_fee_juice_balance_read_hint = - self.build_fee_payer_fee_juice_balance_read_hint(start_public_data_tree); - - let start_archive = NonEmptyMerkleTree::new( - self.pre_existing_blocks, - [0; ARCHIVE_HEIGHT], - [0; ARCHIVE_HEIGHT - 1], - [0; 1], - ); - self.constants.last_archive = AppendOnlyTreeSnapshot { - root: start_archive.get_root(), - next_available_leaf_index: start_archive.get_next_available_index() as u32, - }; - - let (nullifier_predecessor_preimages, nullifier_predecessor_membership_witnesses, sorted_nullifiers, sorted_nullifier_indexes) = self - .update_nullifier_tree_with_new_leaves( - &mut start_nullifier_tree, - &mut kernel_data, - start_nullifier_tree_snapshot, - ); - - let nullifier_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( - start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), - [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], - ); - - let (public_data_sibling_path, sorted_public_data_writes, sorted_public_data_writes_indexes, low_public_data_writes_preimages, low_public_data_writes_witnesses, _new_subtree) = update_public_data_tree( - &mut start_public_data_tree, - &mut kernel_data, - start_public_data_tree_snapshot, - self.public_data_writes, - self.protocol_public_data_writes, - self.final_public_data_writes, - self.pre_existing_public_data, - ); - - let start = PartialStateReference { - note_hash_tree: start_note_hash_tree_snapshot, - nullifier_tree: start_nullifier_tree_snapshot, - public_data_tree: start_public_data_tree_snapshot, - }; - - let state_diff_hints = StateDiffHints { - nullifier_predecessor_preimages, - nullifier_predecessor_membership_witnesses, - sorted_nullifiers, - sorted_nullifier_indexes, - note_hash_subtree_sibling_path, - nullifier_subtree_sibling_path, - public_data_sibling_path, - }; - - BaseRollupInputs { - kernel_data, - start, - state_diff_hints, - sorted_public_data_writes, - sorted_public_data_writes_indexes, - low_public_data_writes_preimages, - low_public_data_writes_witnesses, - archive_root_membership_witness: MembershipWitness { - leaf_index: 0, - sibling_path: start_archive.get_sibling_path(0), - }, - constants: self.constants, - transaction_fee: self.transaction_fee, - fee_payer_fee_juice_balance_read_hint, - } - } - - fn execute(self) -> BaseOrMergeRollupPublicInputs { - let inputs = unsafe { self.build_inputs() }; - inputs.base_rollup_circuit() - } - - fn succeeds(self) { - let _ = self.execute(); - } - - fn fails(self) { - let _ = self.execute(); - } - } - - impl Empty for BaseRollupInputsBuilder { - fn empty() -> Self { - BaseRollupInputsBuilder { - kernel_data: FixtureBuilder::new(), - transaction_fee: 0, - pre_existing_notes: [0; MAX_NOTE_HASHES_PER_TX], - pre_existing_nullifiers: [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX], - pre_existing_contracts: [0; 2], - pre_existing_public_data: [ - PublicDataTreeLeafPreimage::empty(); - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - ], - pre_existing_blocks: [0; 2], - public_data_reads: BoundedVec::new(), - public_data_writes: BoundedVec::new(), - protocol_public_data_writes: BoundedVec::new(), - final_public_data_writes: BoundedVec::new(), - nullifiers: BoundedVec::new(), - constants: ConstantRollupData::empty(), - fee_payer_fee_juice_balance_pre_existing_public_data_index: Option::none(), - } - } - } - - #[test] - unconstrained fn note_hashes_tree() { - let mut builder = BaseRollupInputsBuilder::new(); - - let note_hashes = [27, 28, 29, 30, 31, 32]; - for i in 0..note_hashes.len() { - builder.kernel_data.add_new_note_hash(note_hashes[i]); - } - let mut expected_commitments_tree = NonEmptyMerkleTree::new( - [0; MAX_NOTE_HASHES_PER_TX * 2], - [0; NOTE_HASH_TREE_HEIGHT], - [0; NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT - 1], - [0; NOTE_HASH_SUBTREE_HEIGHT + 1], - ); - - let outputs = builder.execute(); - let expected_start_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { - root: expected_commitments_tree.get_root(), - next_available_leaf_index: MAX_NOTE_HASHES_PER_TX as u32, - }; - assert(outputs.start.note_hash_tree.eq(expected_start_note_hash_tree_snapshot)); - - for i in 0..note_hashes.len() { - expected_commitments_tree.update_leaf(i + MAX_NOTE_HASHES_PER_TX, note_hashes[i]); - } - let expected_end_note_hash_tree_snapshot = AppendOnlyTreeSnapshot { - root: expected_commitments_tree.get_root(), - next_available_leaf_index: (MAX_NOTE_HASHES_PER_TX * 2) as u32, - }; - assert(outputs.end.note_hash_tree.eq(expected_end_note_hash_tree_snapshot)); - } - - #[test] - unconstrained fn new_nullifier_tree_empty() { - // This test checks for insertions of all 0 values - // In this special case we will not need to provide sibling paths to check insertion of the nullifier values - // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left - // empty to begin with. - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = - NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; - builder.pre_existing_nullifiers[1] = - NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; - - builder.succeeds(); - } - - #[test] - unconstrained fn nullifier_insertion_test() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = - NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; - builder.pre_existing_nullifiers[1] = - NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; - - builder.nullifiers.push(NullifierInsertion { existing_index: 0, value: 1 }); - let mut tree_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX * 2]; - tree_nullifiers[0] = NullifierLeafPreimage { - nullifier: 0, - next_nullifier: 1, - next_index: MAX_NULLIFIERS_PER_TX, - }; - tree_nullifiers[1] = builder.pre_existing_nullifiers[1]; - tree_nullifiers[MAX_NULLIFIERS_PER_TX] = - NullifierLeafPreimage { nullifier: 1, next_nullifier: 7, next_index: 1 }; - - let mut end_nullifier_tree = NonEmptyMerkleTree::new( - tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], - [0; NULLIFIER_SUBTREE_HEIGHT + 1], - ); - - let output = builder.execute(); - - assert(output.end.nullifier_tree.eq( - AppendOnlyTreeSnapshot { - root: end_nullifier_tree.get_root(), - next_available_leaf_index: 2 * MAX_NULLIFIERS_PER_TX as u32, - }, - )); - } - - #[test] - unconstrained fn new_nullifier_tree_all_larger() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = - NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; - builder.pre_existing_nullifiers[1] = - NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; - - builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); - for i in 1..builder.nullifiers.max_len() { - builder.nullifiers.push( - NullifierInsertion { existing_index: 1, value: (8 + i) as Field }, - ); - } - - let output = builder.execute(); - let mut tree_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX * 2]; - tree_nullifiers[0] = builder.pre_existing_nullifiers[0]; - - tree_nullifiers[1] = NullifierLeafPreimage { - nullifier: 7, - next_nullifier: 8, - next_index: MAX_NULLIFIERS_PER_TX, - }; - - let last_index = builder.nullifiers.max_len() - 1; - for i in 0..last_index { - tree_nullifiers[MAX_NULLIFIERS_PER_TX + i] = NullifierLeafPreimage { - nullifier: (8 + i) as Field, - next_nullifier: (8 + i + 1) as Field, - next_index: MAX_NULLIFIERS_PER_TX + i + 1, - }; - } - tree_nullifiers[MAX_NULLIFIERS_PER_TX + last_index] = NullifierLeafPreimage { - nullifier: (8 + last_index) as Field, - next_nullifier: 0, - next_index: 0, - }; - - let mut end_nullifier_tree = NonEmptyMerkleTree::new( - tree_nullifiers.map(|preimage: NullifierLeafPreimage| preimage.hash()), - [0; NULLIFIER_TREE_HEIGHT], - [0; NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT - 1], - [0; NULLIFIER_SUBTREE_HEIGHT + 1], - ); - - assert(output.end.nullifier_tree.eq( - AppendOnlyTreeSnapshot { - root: end_nullifier_tree.get_root(), - next_available_leaf_index: 2 * MAX_NULLIFIERS_PER_TX as u32, - }, - )); - } - - #[test(should_fail_with = "Invalid low leaf")] - unconstrained fn new_nullifier_tree_double_spend() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = - NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; - builder.pre_existing_nullifiers[1] = - NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; - - builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); - builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); - - builder.fails(); - } - - #[test(should_fail_with = "Invalid low leaf")] - unconstrained fn new_nullifier_tree_double_spend_same_batch() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_nullifiers[0] = - NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; - builder.pre_existing_nullifiers[1] = - NullifierLeafPreimage { nullifier: 7, next_nullifier: 0, next_index: 0 }; - - builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); - builder.nullifiers.push(NullifierInsertion { existing_index: 1, value: 8 }); - - builder.fails(); - } - - #[test] - unconstrained fn empty_tx_effects_hash() { - let outputs = BaseRollupInputsBuilder::new().execute(); - - let hash_input_flattened = [0; TX_EFFECTS_HASH_INPUT_FIELDS * 32]; - let sha_digest = std::hash::sha256(hash_input_flattened); - let expected_tx_effects_hash = field_from_bytes_32_trunc(sha_digest); - assert_eq(outputs.txs_effects_hash, expected_tx_effects_hash); - } - - #[test] - unconstrained fn empty_block_out_hash() { - let outputs = BaseRollupInputsBuilder::new().execute(); - assert_eq(outputs.out_hash, 0); - } - - #[test] - unconstrained fn nonempty_block_out_hash() { - let mut builder = BaseRollupInputsBuilder::new(); - - for i in 0..MAX_L2_TO_L1_MSGS_PER_TX { - builder.kernel_data.add_exposed_l2_to_l1_message( - i as Field, - EthAddress::from_field(1 + i as Field), - ); - } - - let out_hash = builder.execute().out_hash; - let siloed_l2_to_l1_msgs = builder.kernel_data.l2_to_l1_msgs.map( - |l2_to_l1_message: ScopedL2ToL1Message| silo_l2_to_l1_message( - l2_to_l1_message, - builder.constants.global_variables.version, - builder.constants.global_variables.chain_id, - ), - ); - - // Since we fill the tree completely, we know to expect a full tree as below - let expected_tree = dep::types::merkle_tree::variable_merkle_tree::tests::generate_full_sha_tree( - siloed_l2_to_l1_msgs.storage(), - ); - assert_eq(out_hash, expected_tree.get_root()); - } - - #[test(should_fail_with = "membership check failed")] - unconstrained fn compute_membership_archive_negative() { - let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); - inputs.archive_root_membership_witness.sibling_path[0] = 27; - let _output = inputs.base_rollup_circuit(); - } - - #[test] - unconstrained fn constants_dont_change() { - let inputs = BaseRollupInputsBuilder::new().build_inputs(); - let outputs = inputs.base_rollup_circuit(); - - assert(inputs.constants.eq(outputs.constants)); - } - - #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] - unconstrained fn constants_dont_match_kernels_chain_id() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.chain_id = 3; - builder.fails(); - } - - #[test(should_fail_with = "kernel version does not match the rollup version")] - unconstrained fn constants_dont_match_kernels_version() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.version += 1; - builder.fails(); - } - - #[test(should_fail_with = "kernel global variables do not match the rollup global variables")] - unconstrained fn constants_global_variables_dont_match_kernels() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.kernel_data.global_variables.block_number = 6; - builder.constants.global_variables.block_number = 7; - builder.fails(); - } - - #[test(should_fail_with = "kernel max_block_number is smaller than block number")] - unconstrained fn constants_dont_satisfy_smaller_max_block_number() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.block_number = 42; - builder.kernel_data.set_max_block_number(5); - builder.fails(); - } - - #[test] - unconstrained fn constants_satisfy_equal_max_block_number() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.block_number = 42; - builder.kernel_data.set_max_block_number(42); - builder.succeeds(); - } - - #[test] - unconstrained fn constants_satisfy_larger_max_block_number() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.constants.global_variables.block_number = 42; - builder.kernel_data.set_max_block_number(4294967295); - builder.succeeds(); - } - - #[test] - unconstrained fn num_txs_is_1() { - let outputs = BaseRollupInputsBuilder::new().execute(); - - assert_eq(outputs.num_txs, 1); - } - - #[test] - unconstrained fn single_public_state_write() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_public_data[0] = - PublicDataTreeLeafPreimage { slot: 27, value: 28, next_slot: 0, next_index: 0 }; - builder.public_data_writes.push((0, PublicDataTreeLeaf { slot: 27, value: 29 })); - let outputs = builder.execute(); - - let updated_leaf = - PublicDataTreeLeafPreimage { slot: 27, value: 29, next_slot: 0, next_index: 0 }; - - let mut expected_public_data_tree = NonEmptyMerkleTree::new( - [updated_leaf.hash(), 0], - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - 1], - [0; 1], - ); - - assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); - } - - #[test] - unconstrained fn multiple_public_state_read_writes() { - let mut builder = BaseRollupInputsBuilder::new(); - - builder.pre_existing_public_data[0] = - PublicDataTreeLeafPreimage { slot: 20, value: 40, next_slot: 28, next_index: 1 }; - builder.pre_existing_public_data[1] = - PublicDataTreeLeafPreimage { slot: 28, value: 41, next_slot: 29, next_index: 2 }; - builder.pre_existing_public_data[2] = - PublicDataTreeLeafPreimage { slot: 29, value: 42, next_slot: 30, next_index: 3 }; - builder.pre_existing_public_data[3] = - PublicDataTreeLeafPreimage { slot: 30, value: 43, next_slot: 0, next_index: 0 }; - builder.public_data_reads.push(0); - builder.public_data_writes.push((0, PublicDataTreeLeaf { slot: 25, value: 60 })); - builder.public_data_reads.push(4); - builder.public_data_writes.push((0, PublicDataTreeLeaf { slot: 20, value: 90 })); - - let outputs = builder.execute(); - - let mut public_data_leaves = [0; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2]; - public_data_leaves[0] = PublicDataTreeLeafPreimage { - slot: 20, - value: 90, - next_slot: 25, - next_index: MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - } - .hash(); - public_data_leaves[1] = - PublicDataTreeLeafPreimage { slot: 28, value: 41, next_slot: 29, next_index: 2 }.hash(); - public_data_leaves[2] = - PublicDataTreeLeafPreimage { slot: 29, value: 42, next_slot: 30, next_index: 3 }.hash(); - public_data_leaves[3] = - PublicDataTreeLeafPreimage { slot: 30, value: 43, next_slot: 0, next_index: 0 }.hash(); - public_data_leaves[MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = - PublicDataTreeLeafPreimage { slot: 25, value: 60, next_slot: 28, next_index: 1 }.hash(); - - let mut expected_public_data_tree = NonEmptyMerkleTree::new( - public_data_leaves, - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT - 1], - [0; PUBLIC_DATA_SUBTREE_HEIGHT + 1], - ); - - assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); - } - - #[test] - unconstrained fn updates_fee_payer_balance_with_new_data_write() { - let fee_payer = AztecAddress::from_field(0x1234); - let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - let initial_balance = 300_000; - let tx_fee = 100_000; - let expected_balance = 200_000; - - let mut builder = BaseRollupInputsBuilder::new(); - - // Set fee payer - builder.kernel_data.fee_payer = fee_payer; - - // Set pre-existing balance - builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { - slot: balance_slot, - value: initial_balance, - next_slot: 0, - next_index: 0, - }; - builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); - - // Set values for computing exact tx_fee - builder.transaction_fee = tx_fee; - - // Set expected protocol data update - builder.protocol_public_data_writes.push(( - 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, - )); - - let outputs = builder.execute(); - - // The new public data tree should have updated the balance of the fee payer - let updated_leaf = PublicDataTreeLeafPreimage { - slot: balance_slot, - value: expected_balance, - next_slot: 0, - next_index: 0, - }; - let mut expected_public_data_tree = NonEmptyMerkleTree::new( - [updated_leaf.hash(), 0], - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - 1], - [0; 1], - ); - - assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); - } - - #[test] - unconstrained fn updates_fee_payer_balance_in_existing_data_write() { - let fee_payer = AztecAddress::from_field(0x1234); - let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - let initial_balance = 100_000; - let after_claim_balance = 300_000; - let tx_fee = 100_000; - let expected_balance = 200_000; - - let mut builder = BaseRollupInputsBuilder::new(); - - // Set fee payer - builder.kernel_data.fee_payer = fee_payer; - - // Set pre-existing balance, but set no hint for it since we'll update a user update request - builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { - slot: balance_slot, - value: initial_balance, - next_slot: 0, - next_index: 0, - }; - - // Set values for computing exact tx_fee - builder.transaction_fee = tx_fee; - - // Create an existing data update that corresponds to a claim - builder.public_data_writes.push(( - 0, PublicDataTreeLeaf { slot: balance_slot, value: after_claim_balance }, - )); - - // Set expected data updates after base rollup runs - // Note that we tweak the final_public_data_writes directly, since we need to overwrite the output of the user public_data_writes - builder.final_public_data_writes.push(( - 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, - )); - - let outputs = builder.execute(); - - // The new public data tree should have updated the balance of the fee payer - let updated_leaf = PublicDataTreeLeafPreimage { - slot: balance_slot, - value: expected_balance, - next_slot: 0, - next_index: 0, - }; - let mut expected_public_data_tree = NonEmptyMerkleTree::new( - [updated_leaf.hash(), 0], - [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - 1], - [0; 1], - ); - - assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); - } - - #[test(should_fail_with = "Not enough balance for fee payer to pay for transaction")] - unconstrained fn fails_to_update_fee_payer_balance_if_not_enough_funds() { - let fee_payer = AztecAddress::from_field(0x1234); - let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - // Set low initial balance so it fails! - let initial_balance = 10_000; - let tx_fee = 100_000; - - let mut builder = BaseRollupInputsBuilder::new(); - - // Set fee payer - builder.kernel_data.fee_payer = fee_payer; - - // Set pre-existing balance - builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { - slot: balance_slot, - value: initial_balance, - next_slot: 0, - next_index: 0, - }; - builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(0); - - // Set values for computing exact tx_fee - builder.transaction_fee = tx_fee; - - // Set expected protocol data update - builder.protocol_public_data_writes.push(( - 0, PublicDataTreeLeaf { slot: balance_slot, value: -90_000 }, - )); - - builder.fails(); - } - - #[test(should_fail_with = "Wrong leaf slot for Fee Juice balance read hint")] - unconstrained fn fails_to_update_fee_payer_balance_if_wrong_read_hint() { - let fee_payer = AztecAddress::from_field(0x1234); - let balance_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); - let initial_balance = 300_000; - let expected_balance = 200_000; - let tx_fee = 100_000; - - let mut builder = BaseRollupInputsBuilder::new(); - - // Set fee payer - builder.kernel_data.fee_payer = fee_payer; - - // Set pre-existing balance in index 0 - builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { - slot: balance_slot, - value: initial_balance, - next_slot: 0, - next_index: 0, - }; - - builder.pre_existing_public_data[1] = PublicDataTreeLeafPreimage { - slot: 1, - value: initial_balance, - next_slot: balance_slot, - next_index: 0, - }; - - // But point the read hint to the wrong one! - builder.fee_payer_fee_juice_balance_pre_existing_public_data_index = Option::some(1); - - // Set values for computing exact tx_fee - builder.transaction_fee = tx_fee; - - // Set expected protocol data update - builder.protocol_public_data_writes.push(( - 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, - )); - - builder.fails(); - } - - #[test] - fn valid_previous_kernel_empty() { - let builder = unsafe { - BaseRollupInputsBuilder::new_with_previous_kernel(PRIVATE_KERNEL_EMPTY_INDEX) - }; - - let _res = builder.execute(); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/avm_proof_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/avm_proof_data.nr deleted file mode 100644 index 5a2aefbf616..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/avm_proof_data.nr +++ /dev/null @@ -1,17 +0,0 @@ -use dep::types::{ - abis::avm_circuit_public_inputs::AvmCircuitPublicInputs, - constants::AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, - proof::{avm_proof::AvmProof, traits::Verifiable, vk_data::VkData}, -}; - -pub struct AvmProofData { - public_inputs: AvmCircuitPublicInputs, - proof: AvmProof, - vk_data: VkData, -} - -impl Verifiable for AvmProofData { - fn verify(self) { - // TODO(#8470) - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr index 719f03148a9..75763a12f58 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr @@ -1,4 +1,3 @@ -pub mod avm_proof_data; pub(crate) mod public_data_tree; pub(crate) mod nullifier_tree; pub(crate) mod fees; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 9ae9593f6c3..0b05ff8c260 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -313,9 +313,10 @@ mod tests { value: Field, } - global MAX_nullifiers_PER_TEST: u32 = 4; + global MAX_NULLIFIERS_PER_TEST: u32 = 4; global AVAILABLE_PUBLIC_DATA_LEAVES_FOR_TEST = 64; global AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST = 6; + global PRE_EXISTING_PUBLIC_DATA_LEAVES = 10; fn update_public_data_tree( public_data_tree: &mut NonEmptyMerkleTree, @@ -340,7 +341,7 @@ mod tests { slot: low_leaf.slot, value: low_leaf.value, next_slot: fee_write.slot, - next_index: MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + next_index: PRE_EXISTING_PUBLIC_DATA_LEAVES, }; } let low_public_data_writes_witness = MembershipWitness { @@ -372,11 +373,11 @@ mod tests { pre_existing_notes: [Field; MAX_NOTE_HASHES_PER_TX], pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], pre_existing_contracts: [Field; 2], - pre_existing_public_data: [PublicDataTreeLeafPreimage; 10], + pre_existing_public_data: [PublicDataTreeLeafPreimage; PRE_EXISTING_PUBLIC_DATA_LEAVES], pre_existing_blocks: [Field; 2], fee_write: (u32, PublicDataTreeLeaf), - nullifiers: BoundedVec, + nullifiers: BoundedVec, constants: ConstantRollupData, // Index of the item in the pre_existing_public_data array that contains the fee payer's Fee Juice balance. // Used for building the public data hint read for the payment update request. If set to none, no hint is built. @@ -459,7 +460,7 @@ mod tests { let mut sorted_nullifiers_indexes = [0; MAX_NULLIFIERS_PER_TX]; for i in 0..MAX_NULLIFIERS_PER_TX { - if (i as u32) < (MAX_nullifiers_PER_TEST as u32) { + if (i as u32) < (MAX_NULLIFIERS_PER_TEST as u32) { sorted_nullifiers[i] = sorted_new_nullifier_tuples[i].elem; sorted_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; } else { @@ -470,7 +471,7 @@ mod tests { let mut pre_existing_nullifiers = self.pre_existing_nullifiers; - for i in 0..MAX_nullifiers_PER_TEST { + for i in 0..MAX_NULLIFIERS_PER_TEST { if i < self.nullifiers.len() { let sorted_tuple = sorted_new_nullifier_tuples[i]; let new_nullifier = sorted_tuple.elem; @@ -635,7 +636,9 @@ mod tests { pre_existing_notes: [0; MAX_NOTE_HASHES_PER_TX], pre_existing_nullifiers: [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX], pre_existing_contracts: [0; 2], - pre_existing_public_data: [PublicDataTreeLeafPreimage::empty(); 10], + pre_existing_public_data: [ + PublicDataTreeLeafPreimage::empty(); PRE_EXISTING_PUBLIC_DATA_LEAVES + ], fee_write: (0, PublicDataTreeLeaf::empty()), pre_existing_blocks: [0; 2], nullifiers: BoundedVec::new(), diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 92c21f4d14f..a8f3af87d10 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -5,7 +5,7 @@ use crate::{ }, base::{ components::{ - avm_proof_data::AvmProofData, fees::compute_fee_payer_fee_juice_balance_leaf_slot, + fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, }, state_diff_hints::PublicBaseStateDiffHints, @@ -16,6 +16,7 @@ use dep::types::{ abis::{ accumulated_data::CombinedAccumulatedData, append_only_tree_snapshot::AppendOnlyTreeSnapshot, + avm::AvmProofData, combined_constant_data::CombinedConstantData, kernel_circuit_public_inputs::KernelCircuitPublicInputs, log_hash::{LogHash, ScopedLogHash}, @@ -448,7 +449,7 @@ mod tests { messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, tests::{fixture_builder::FixtureBuilder, fixtures, merkle_tree_utils::NonEmptyMerkleTree}, - traits::Empty, + traits::{Empty, is_empty}, utils::{ arrays::get_sorted_tuple::get_sorted_tuple, field::{field_from_bytes_32_trunc, full_field_less_than}, @@ -460,33 +461,96 @@ mod tests { value: Field, } - global MAX_nullifiers_PER_TEST: u32 = 4; - global MAX_PUBLIC_DATA_READS_PER_TEST: u32 = 2; + global MAX_NULLIFIERS_PER_TEST: u32 = 4; + global AVAILABLE_PUBLIC_DATA_LEAVES_FOR_TEST = 128; + global AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST = 7; + global PRE_EXISTING_PUBLIC_DATA_LEAVES = 10; + + fn update_public_data_tree( + public_data_tree: &mut NonEmptyMerkleTree, + snapshot: AppendOnlyTreeSnapshot, + writes: [(u32, PublicDataTreeLeaf); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + mut pre_existing_public_data: [PublicDataTreeLeafPreimage; EXISTING_LEAVES], + ) -> ([PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]) { + let mut low_leaves = + [PublicDataTreeLeafPreimage::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; + let mut low_public_data_writes_witnesses = + [MembershipWitness::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; + let mut insertion_witnesses = + [[0; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; + + let mut current_next_leaf_index = snapshot.next_available_leaf_index; + + for i in 0..writes.len() { + let (low_leaf_index, write) = writes[i]; + if (!is_empty(write)) { + let low_leaf = pre_existing_public_data[low_leaf_index]; + let mut new_leaf = PublicDataTreeLeafPreimage::empty(); + if low_leaf.slot == write.slot { + pre_existing_public_data[low_leaf_index].value = write.value; + } else { + new_leaf = PublicDataTreeLeafPreimage { + slot: write.slot, + value: write.value, + next_slot: low_leaf.next_slot, + next_index: low_leaf.next_index, + }; + pre_existing_public_data[low_leaf_index] = PublicDataTreeLeafPreimage { + slot: low_leaf.slot, + value: low_leaf.value, + next_slot: write.slot, + next_index: PRE_EXISTING_PUBLIC_DATA_LEAVES + i, + }; + } + let low_public_data_writes_witness = MembershipWitness { + leaf_index: low_leaf_index as Field, + sibling_path: public_data_tree.get_sibling_path(low_leaf_index), + }; + + public_data_tree.update_leaf( + low_leaf_index, + pre_existing_public_data[low_leaf_index].hash(), + ); - struct BaseRollupInputsBuilder { + let insertion_witness = public_data_tree.get_sibling_path(current_next_leaf_index); + + public_data_tree.update_leaf(current_next_leaf_index, new_leaf.hash()); + + low_leaves[i] = low_leaf; + low_public_data_writes_witnesses[i] = low_public_data_writes_witness; + insertion_witnesses[i] = insertion_witness; + current_next_leaf_index += 1; + } + } + (low_leaves, low_public_data_writes_witnesses, insertion_witnesses) + } + + struct PublicBaseRollupInputsBuilder { tube_data: FixtureBuilder, avm_data: FixtureBuilder, + reverted: bool, + transaction_fee: Field, pre_existing_notes: [Field; MAX_NOTE_HASHES_PER_TX], pre_existing_nullifiers: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], pre_existing_contracts: [Field; 2], - pre_existing_public_data: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + pre_existing_public_data: [PublicDataTreeLeafPreimage; PRE_EXISTING_PUBLIC_DATA_LEAVES], pre_existing_blocks: [Field; 2], // Public data writes generated by app code public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - // Public data writes to be created by the protocol (eg a data update request for updating fee payer balance) + // Public data writes overwritten by the base rollup circuit + overwritten_public_data_writes: [Option; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + // New public data writes to be created by the protocol (eg a data update request for updating fee payer balance) protocol_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - // Public data writes after processing by the base rollup circuit (defaults to public_data_writes ++ protocol_public_data_writes if empty) - final_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>, - nullifiers: BoundedVec, + nullifiers: BoundedVec, constants: ConstantRollupData, // Index of the item in the pre_existing_public_data array that contains the fee payer's Fee Juice balance. // Used for building the public data hint read for the payment update request. If set to none, no hint is built. fee_payer_fee_juice_balance_pre_existing_public_data_index: Option, } - impl BaseRollupInputsBuilder { + impl PublicBaseRollupInputsBuilder { fn new() -> Self { - let mut inputs = BaseRollupInputsBuilder::empty(); + let mut inputs = PublicBaseRollupInputsBuilder::empty(); inputs.tube_data = FixtureBuilder::new().in_vk_tree(TUBE_VK_INDEX); inputs.avm_data = FixtureBuilder::new().in_vk_tree(AVM_VK_INDEX); @@ -500,14 +564,14 @@ mod tests { } unconstrained fn new_with_previous_kernel(previous_vk_index: u32) -> Self { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.tube_data = builder.tube_data.in_vk_tree(previous_vk_index); builder } fn build_fee_payer_fee_juice_balance_read_hint( self, - start_public_data_tree: NonEmptyMerkleTree, + start_public_data_tree: NonEmptyMerkleTree, ) -> PublicDataHint { self.fee_payer_fee_juice_balance_pre_existing_public_data_index.map_or( PublicDataHint::empty(), @@ -543,7 +607,6 @@ mod tests { fn update_nullifier_tree_with_new_leaves( mut self, nullifier_tree: &mut NonEmptyMerkleTree, - kernel_data: &mut KernelData, start_nullifier_tree_snapshot: AppendOnlyTreeSnapshot, ) -> ([NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], [MembershipWitness; MAX_NULLIFIERS_PER_TX], [Field; MAX_NULLIFIERS_PER_TX], [u32; MAX_NULLIFIERS_PER_TX]) { let mut nullifier_predecessor_preimages = @@ -562,7 +625,7 @@ mod tests { let mut sorted_nullifiers_indexes = [0; MAX_NULLIFIERS_PER_TX]; for i in 0..MAX_NULLIFIERS_PER_TX { - if (i as u32) < (MAX_nullifiers_PER_TEST as u32) { + if (i as u32) < (MAX_NULLIFIERS_PER_TEST as u32) { sorted_nullifiers[i] = sorted_new_nullifier_tuples[i].elem; sorted_nullifiers_indexes[i] = sorted_new_nullifier_tuples[i].original_index; } else { @@ -573,7 +636,7 @@ mod tests { let mut pre_existing_nullifiers = self.pre_existing_nullifiers; - for i in 0..MAX_nullifiers_PER_TEST { + for i in 0..MAX_NULLIFIERS_PER_TEST { if i < self.nullifiers.len() { let sorted_tuple = sorted_new_nullifier_tuples[i]; let new_nullifier = sorted_tuple.elem; @@ -581,8 +644,6 @@ mod tests { let low_index = self.nullifiers.get_unchecked(original_index).existing_index; - kernel_data.public_inputs.end.nullifiers[original_index] = new_nullifier; - let mut low_preimage = pre_existing_nullifiers[low_index]; nullifier_predecessor_preimages[i] = low_preimage; low_nullifier_membership_witness[i] = MembershipWitness { @@ -606,9 +667,11 @@ mod tests { ) } - unconstrained fn build_inputs(mut self) -> BaseRollupInputs { - let mut kernel_data = - KernelData { public_inputs: self.kernel_data.to_kernel_circuit_public_inputs() }; + unconstrained fn build_inputs(mut self) -> PublicBaseRollupInputs { + let mut tube_data = self.tube_data.to_public_tube_data(); + let mut avm_proof_data = self.avm_data.to_avm_proof_data(self.reverted); + + avm_proof_data.public_inputs.transaction_fee = self.transaction_fee; let start_note_hash_tree = NonEmptyMerkleTree::new( self.pre_existing_notes, @@ -620,7 +683,7 @@ mod tests { root: start_note_hash_tree.get_root(), next_available_leaf_index: start_note_hash_tree.get_next_available_index() as u32, }; - let note_hash_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( + let note_hash_subtree_sibling_path = PublicBaseRollupInputsBuilder::extract_subtree_sibling_path( start_note_hash_tree.get_sibling_path(self.pre_existing_notes.len()), [0; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], ); @@ -637,17 +700,21 @@ mod tests { next_available_leaf_index: start_nullifier_tree.get_next_available_index() as u32, }; + let mut pre_existing_leaves = [0; AVAILABLE_PUBLIC_DATA_LEAVES_FOR_TEST]; + + for i in 0..self.pre_existing_public_data.len() { + pre_existing_leaves[i] = self.pre_existing_public_data[i].hash(); + } + let mut start_public_data_tree = NonEmptyMerkleTree::new( - self.pre_existing_public_data.map(|preimage: PublicDataTreeLeafPreimage| { - preimage.hash() - }), + pre_existing_leaves, [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT], - [0; PUBLIC_DATA_SUBTREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST], + [0; AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST], ); let start_public_data_tree_snapshot = AppendOnlyTreeSnapshot { root: start_public_data_tree.get_root(), - next_available_leaf_index: start_public_data_tree.get_next_available_index() as u32, + next_available_leaf_index: self.pre_existing_public_data.len(), }; let fee_payer_fee_juice_balance_read_hint = @@ -667,62 +734,83 @@ mod tests { let (nullifier_predecessor_preimages, nullifier_predecessor_membership_witnesses, sorted_nullifiers, sorted_nullifier_indexes) = self .update_nullifier_tree_with_new_leaves( &mut start_nullifier_tree, - &mut kernel_data, start_nullifier_tree_snapshot, ); - let nullifier_subtree_sibling_path = BaseRollupInputsBuilder::extract_subtree_sibling_path( + for i in 0..self.nullifiers.len() { + let nullifier = self.nullifiers.get_unchecked(i); + avm_proof_data.public_inputs.accumulated_data.nullifiers[i] = nullifier.value; + } + + let nullifier_subtree_sibling_path = PublicBaseRollupInputsBuilder::extract_subtree_sibling_path( start_nullifier_tree.get_sibling_path(self.pre_existing_nullifiers.len()), [0; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], ); - let (public_data_sibling_path, sorted_public_data_writes, sorted_public_data_writes_indexes, low_public_data_writes_preimages, low_public_data_writes_witnesses, _new_subtree) = update_public_data_tree( + let mut final_public_data_writes: BoundedVec<(u32, PublicDataTreeLeaf), MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX> = + BoundedVec::new(); + final_public_data_writes.extend_from_array(self.public_data_writes.storage); + final_public_data_writes.extend_from_array(self.protocol_public_data_writes.storage); + for i in 0..self.overwritten_public_data_writes.len() { + if self.overwritten_public_data_writes[i].is_some() { + final_public_data_writes.set( + i, + ( + final_public_data_writes.get(i).0, + self.overwritten_public_data_writes[i].unwrap_unchecked(), + ), + ); + } + } + + let (low_public_data_writes_preimages, low_public_data_writes_witnesses, public_data_tree_sibling_paths) = update_public_data_tree( &mut start_public_data_tree, - &mut kernel_data, start_public_data_tree_snapshot, - self.public_data_writes, - self.protocol_public_data_writes, - self.final_public_data_writes, + final_public_data_writes.storage(), self.pre_existing_public_data, ); + for i in 0..self.public_data_writes.len() { + let leaf = self.public_data_writes.get_unchecked(i).1; + avm_proof_data.public_inputs.accumulated_data.public_data_writes[i] = + PublicDataWrite { leaf_slot: leaf.slot, value: leaf.value }; + } + let start = PartialStateReference { note_hash_tree: start_note_hash_tree_snapshot, nullifier_tree: start_nullifier_tree_snapshot, public_data_tree: start_public_data_tree_snapshot, }; - let state_diff_hints = StateDiffHints { + let state_diff_hints = PublicBaseStateDiffHints { nullifier_predecessor_preimages, nullifier_predecessor_membership_witnesses, sorted_nullifiers, sorted_nullifier_indexes, note_hash_subtree_sibling_path, nullifier_subtree_sibling_path, - public_data_sibling_path, + low_public_data_writes_preimages, + low_public_data_writes_witnesses, + public_data_tree_sibling_paths, }; - BaseRollupInputs { - kernel_data, + PublicBaseRollupInputs { + tube_data, + avm_proof_data, start, state_diff_hints, - sorted_public_data_writes, - sorted_public_data_writes_indexes, - low_public_data_writes_preimages, - low_public_data_writes_witnesses, archive_root_membership_witness: MembershipWitness { leaf_index: 0, sibling_path: start_archive.get_sibling_path(0), }, constants: self.constants, - transaction_fee: self.transaction_fee, fee_payer_fee_juice_balance_read_hint, } } fn execute(self) -> BaseOrMergeRollupPublicInputs { let inputs = unsafe { self.build_inputs() }; - inputs.base_rollup_circuit() + inputs.execute() } fn succeeds(self) { @@ -734,36 +822,39 @@ mod tests { } } - impl Empty for BaseRollupInputsBuilder { + impl Empty for PublicBaseRollupInputsBuilder { fn empty() -> Self { - BaseRollupInputsBuilder { - kernel_data: FixtureBuilder::new(), + Self { + tube_data: FixtureBuilder::empty(), + avm_data: FixtureBuilder::empty(), transaction_fee: 0, + reverted: false, pre_existing_notes: [0; MAX_NOTE_HASHES_PER_TX], pre_existing_nullifiers: [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX], pre_existing_contracts: [0; 2], pre_existing_public_data: [ - PublicDataTreeLeafPreimage::empty(); - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX - ], - pre_existing_blocks: [0; 2], - public_data_writes: BoundedVec::new(), - protocol_public_data_writes: BoundedVec::new(), - final_public_data_writes: BoundedVec::new(), - nullifiers: BoundedVec::new(), - constants: ConstantRollupData::empty(), - fee_payer_fee_juice_balance_pre_existing_public_data_index: Option::none(), - } + PublicDataTreeLeafPreimage::empty(); PRE_EXISTING_PUBLIC_DATA_LEAVES + ], + pre_existing_blocks: [0; 2], + public_data_writes: BoundedVec::new(), + overwritten_public_data_writes: [ + Option::none(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + ], + protocol_public_data_writes: BoundedVec::new(), + nullifiers: BoundedVec::new(), + constants: ConstantRollupData::empty(), + fee_payer_fee_juice_balance_pre_existing_public_data_index: Option::none(), } + } } #[test] unconstrained fn note_hashes_tree() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); let note_hashes = [27, 28, 29, 30, 31, 32]; for i in 0..note_hashes.len() { - builder.kernel_data.add_new_note_hash(note_hashes[i]); + builder.avm_data.add_new_note_hash(note_hashes[i]); } let mut expected_commitments_tree = NonEmptyMerkleTree::new( [0; MAX_NOTE_HASHES_PER_TX * 2], @@ -795,7 +886,7 @@ mod tests { // In this special case we will not need to provide sibling paths to check insertion of the nullifier values // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left // empty to begin with. - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; @@ -807,7 +898,7 @@ mod tests { #[test] unconstrained fn nullifier_insertion_test() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; @@ -844,7 +935,7 @@ mod tests { #[test] unconstrained fn new_nullifier_tree_all_larger() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; @@ -899,7 +990,7 @@ mod tests { #[test(should_fail_with = "Invalid low leaf")] unconstrained fn new_nullifier_tree_double_spend() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; @@ -914,7 +1005,7 @@ mod tests { #[test(should_fail_with = "Invalid low leaf")] unconstrained fn new_nullifier_tree_double_spend_same_batch() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 7, next_index: 1 }; @@ -929,7 +1020,7 @@ mod tests { #[test] unconstrained fn empty_tx_effects_hash() { - let outputs = BaseRollupInputsBuilder::new().execute(); + let outputs = PublicBaseRollupInputsBuilder::new().execute(); let hash_input_flattened = [0; TX_EFFECTS_HASH_INPUT_FIELDS * 32]; let sha_digest = std::hash::sha256(hash_input_flattened); @@ -939,23 +1030,23 @@ mod tests { #[test] unconstrained fn empty_block_out_hash() { - let outputs = BaseRollupInputsBuilder::new().execute(); + let outputs = PublicBaseRollupInputsBuilder::new().execute(); assert_eq(outputs.out_hash, 0); } #[test] unconstrained fn nonempty_block_out_hash() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); for i in 0..MAX_L2_TO_L1_MSGS_PER_TX { - builder.kernel_data.add_exposed_l2_to_l1_message( + builder.avm_data.add_exposed_l2_to_l1_message( i as Field, EthAddress::from_field(1 + i as Field), ); } let out_hash = builder.execute().out_hash; - let siloed_l2_to_l1_msgs = builder.kernel_data.l2_to_l1_msgs.map( + let siloed_l2_to_l1_msgs = builder.avm_data.l2_to_l1_msgs.map( |l2_to_l1_message: ScopedL2ToL1Message| silo_l2_to_l1_message( l2_to_l1_message, builder.constants.global_variables.version, @@ -972,75 +1063,75 @@ mod tests { #[test(should_fail_with = "membership check failed")] unconstrained fn compute_membership_archive_negative() { - let mut inputs = BaseRollupInputsBuilder::new().build_inputs(); + let mut inputs = PublicBaseRollupInputsBuilder::new().build_inputs(); inputs.archive_root_membership_witness.sibling_path[0] = 27; - let _output = inputs.base_rollup_circuit(); + let _output = inputs.execute(); } #[test] unconstrained fn constants_dont_change() { - let inputs = BaseRollupInputsBuilder::new().build_inputs(); - let outputs = inputs.base_rollup_circuit(); + let inputs = PublicBaseRollupInputsBuilder::new().build_inputs(); + let outputs = inputs.execute(); assert(inputs.constants.eq(outputs.constants)); } #[test(should_fail_with = "kernel chain_id does not match the rollup chain_id")] unconstrained fn constants_dont_match_kernels_chain_id() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.constants.global_variables.chain_id = 3; builder.fails(); } #[test(should_fail_with = "kernel version does not match the rollup version")] unconstrained fn constants_dont_match_kernels_version() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.constants.global_variables.version += 1; builder.fails(); } #[test(should_fail_with = "kernel global variables do not match the rollup global variables")] unconstrained fn constants_global_variables_dont_match_kernels() { - let mut builder = BaseRollupInputsBuilder::new(); - builder.kernel_data.global_variables.block_number = 6; + let mut builder = PublicBaseRollupInputsBuilder::new(); + builder.avm_data.global_variables.block_number = 6; builder.constants.global_variables.block_number = 7; builder.fails(); } #[test(should_fail_with = "kernel max_block_number is smaller than block number")] unconstrained fn constants_dont_satisfy_smaller_max_block_number() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.constants.global_variables.block_number = 42; - builder.kernel_data.set_max_block_number(5); + builder.tube_data.set_max_block_number(5); builder.fails(); } #[test] unconstrained fn constants_satisfy_equal_max_block_number() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.constants.global_variables.block_number = 42; - builder.kernel_data.set_max_block_number(42); + builder.tube_data.set_max_block_number(42); builder.succeeds(); } #[test] unconstrained fn constants_satisfy_larger_max_block_number() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.constants.global_variables.block_number = 42; - builder.kernel_data.set_max_block_number(4294967295); + builder.tube_data.set_max_block_number(4294967295); builder.succeeds(); } #[test] unconstrained fn num_txs_is_1() { - let outputs = BaseRollupInputsBuilder::new().execute(); + let outputs = PublicBaseRollupInputsBuilder::new().execute(); assert_eq(outputs.num_txs, 1); } #[test] unconstrained fn single_public_state_write() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { slot: 27, value: 28, next_slot: 0, next_index: 0 }; @@ -1062,7 +1153,7 @@ mod tests { #[test] unconstrained fn multiple_public_state_read_writes() { - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { slot: 20, value: 40, next_slot: 28, next_index: 1 }; @@ -1077,12 +1168,12 @@ mod tests { let outputs = builder.execute(); - let mut public_data_leaves = [0; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2]; + let mut public_data_leaves = [0; AVAILABLE_PUBLIC_DATA_LEAVES_FOR_TEST]; public_data_leaves[0] = PublicDataTreeLeafPreimage { slot: 20, value: 90, next_slot: 25, - next_index: MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + next_index: PRE_EXISTING_PUBLIC_DATA_LEAVES, } .hash(); public_data_leaves[1] = @@ -1091,14 +1182,14 @@ mod tests { PublicDataTreeLeafPreimage { slot: 29, value: 42, next_slot: 30, next_index: 3 }.hash(); public_data_leaves[3] = PublicDataTreeLeafPreimage { slot: 30, value: 43, next_slot: 0, next_index: 0 }.hash(); - public_data_leaves[MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = + public_data_leaves[PRE_EXISTING_PUBLIC_DATA_LEAVES] = PublicDataTreeLeafPreimage { slot: 25, value: 60, next_slot: 28, next_index: 1 }.hash(); let mut expected_public_data_tree = NonEmptyMerkleTree::new( public_data_leaves, [0; PUBLIC_DATA_TREE_HEIGHT], - [0; PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT - 1], - [0; PUBLIC_DATA_SUBTREE_HEIGHT + 1], + [0; PUBLIC_DATA_TREE_HEIGHT - AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST], + [0; AVAILABLE_PUBLIC_DATA_SUBTREE_HEIGHT_FOR_TEST], ); assert_eq(outputs.end.public_data_tree.root, expected_public_data_tree.get_root()); @@ -1112,10 +1203,10 @@ mod tests { let tx_fee = 100_000; let expected_balance = 200_000; - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); // Set fee payer - builder.kernel_data.fee_payer = fee_payer; + builder.tube_data.fee_payer = fee_payer; // Set pre-existing balance builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { @@ -1162,10 +1253,10 @@ mod tests { let tx_fee = 100_000; let expected_balance = 200_000; - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); // Set fee payer - builder.kernel_data.fee_payer = fee_payer; + builder.tube_data.fee_payer = fee_payer; // Set pre-existing balance, but set no hint for it since we'll update a user update request builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { @@ -1178,17 +1269,16 @@ mod tests { // Set values for computing exact tx_fee builder.transaction_fee = tx_fee; + // Rollup will overwrite the public data write we are about to push + builder.overwritten_public_data_writes[builder.public_data_writes.len()] = Option::some( + PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, + ); + // Create an existing data update that corresponds to a claim builder.public_data_writes.push(( 0, PublicDataTreeLeaf { slot: balance_slot, value: after_claim_balance }, )); - // Set expected data updates after base rollup runs - // Note that we tweak the final_public_data_writes directly, since we need to overwrite the output of the user public_data_writes - builder.final_public_data_writes.push(( - 0, PublicDataTreeLeaf { slot: balance_slot, value: expected_balance }, - )); - let outputs = builder.execute(); // The new public data tree should have updated the balance of the fee payer @@ -1216,10 +1306,10 @@ mod tests { let initial_balance = 10_000; let tx_fee = 100_000; - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); // Set fee payer - builder.kernel_data.fee_payer = fee_payer; + builder.tube_data.fee_payer = fee_payer; // Set pre-existing balance builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { @@ -1249,10 +1339,10 @@ mod tests { let expected_balance = 200_000; let tx_fee = 100_000; - let mut builder = BaseRollupInputsBuilder::new(); + let mut builder = PublicBaseRollupInputsBuilder::new(); // Set fee payer - builder.kernel_data.fee_payer = fee_payer; + builder.tube_data.fee_payer = fee_payer; // Set pre-existing balance in index 0 builder.pre_existing_public_data[0] = PublicDataTreeLeafPreimage { @@ -1286,7 +1376,7 @@ mod tests { #[test] fn valid_previous_kernel_empty() { let builder = unsafe { - BaseRollupInputsBuilder::new_with_previous_kernel(PRIVATE_KERNEL_EMPTY_INDEX) + PublicBaseRollupInputsBuilder::new_with_previous_kernel(PRIVATE_KERNEL_EMPTY_INDEX) }; let _res = builder.execute(); diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr index c9440abad08..65d27104795 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/state_diff_hints.nr @@ -9,38 +9,38 @@ use dep::types::{ merkle_tree::MembershipWitness, }; -pub struct PrivateBaseStateDiffHints { - nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], - nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], +pub(crate) struct PrivateBaseStateDiffHints { + pub(crate) nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], + pub(crate) nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], - sorted_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], - sorted_nullifier_indexes: [u32; MAX_NULLIFIERS_PER_TX], + pub(crate) sorted_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], + pub(crate) sorted_nullifier_indexes: [u32; MAX_NULLIFIERS_PER_TX], // For inserting the new subtrees into their respective trees: // Note: the insertion leaf index can be derived from the snapshots' `next_available_leaf_index` values (tree // snapshots of the relevant trees are stored in partial state reference). - note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], - nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + pub(crate) note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], + pub(crate) nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], - fee_write_low_leaf_preimage: PublicDataTreeLeafPreimage, - fee_write_low_leaf_membership_witness: MembershipWitness, - fee_write_sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT], + pub(crate) fee_write_low_leaf_preimage: PublicDataTreeLeafPreimage, + pub(crate) fee_write_low_leaf_membership_witness: MembershipWitness, + pub(crate) fee_write_sibling_path: [Field; PUBLIC_DATA_TREE_HEIGHT], } -pub struct PublicBaseStateDiffHints { - nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], - nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], +pub(crate) struct PublicBaseStateDiffHints { + pub(crate) nullifier_predecessor_preimages: [NullifierLeafPreimage; MAX_NULLIFIERS_PER_TX], + pub(crate) nullifier_predecessor_membership_witnesses: [MembershipWitness; MAX_NULLIFIERS_PER_TX], - sorted_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], - sorted_nullifier_indexes: [u32; MAX_NULLIFIERS_PER_TX], + pub(crate) sorted_nullifiers: [Field; MAX_NULLIFIERS_PER_TX], + pub(crate) sorted_nullifier_indexes: [u32; MAX_NULLIFIERS_PER_TX], // For inserting the new subtrees into their respective trees: // Note: the insertion leaf index can be derived from the snapshots' `next_available_leaf_index` values (tree // snapshots of the relevant trees are stored in partial state reference). - note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], - nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], + pub(crate) note_hash_subtree_sibling_path: [Field; NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH], + pub(crate) nullifier_subtree_sibling_path: [Field; NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH], - low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], - public_data_tree_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + pub(crate) low_public_data_writes_preimages: [PublicDataTreeLeafPreimage; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + pub(crate) low_public_data_writes_witnesses: [MembershipWitness; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], + pub(crate) public_data_tree_sibling_paths: [[Field; PUBLIC_DATA_TREE_HEIGHT]; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/avm_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/avm.nr similarity index 94% rename from noir-projects/noir-protocol-circuits/crates/types/src/abis/avm_circuit_public_inputs.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/abis/avm.nr index 1ce06e8f53a..7affd9031c9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/avm_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/avm.nr @@ -12,7 +12,11 @@ use crate::{ public_call_request::PublicCallRequest, tree_snapshots::TreeSnapshots, }, - constants::{AVM_CIRCUIT_PUBLIC_INPUTS_LENGTH, MAX_ENQUEUED_CALLS_PER_TX}, + constants::{ + AVM_CIRCUIT_PUBLIC_INPUTS_LENGTH, AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, + MAX_ENQUEUED_CALLS_PER_TX, + }, + proof::{avm_proof::AvmProof, traits::Verifiable, vk_data::VkData}, traits::{Deserialize, Empty, Serialize}, utils::reader::Reader, }; @@ -170,6 +174,18 @@ impl Deserialize for AvmCircuitPublicInputs { } } +pub struct AvmProofData { + pub public_inputs: AvmCircuitPublicInputs, + pub proof: AvmProof, + pub vk_data: VkData, +} + +impl Verifiable for AvmProofData { + fn verify(self) { + // TODO(#8470) + } +} + #[test] fn serialization_of_empty_avm_circuit_public_inputs() { let item = AvmCircuitPublicInputs::empty(); @@ -177,3 +193,4 @@ fn serialization_of_empty_avm_circuit_public_inputs() { let deserialized = AvmCircuitPublicInputs::deserialize(serialized); assert(item.eq(deserialized)); } + diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr index 1365fff4b8a..8201d77d69e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr @@ -1,5 +1,5 @@ pub mod append_only_tree_snapshot; -pub mod avm_circuit_public_inputs; +pub mod avm; pub mod contract_class_function_leaf_preimage; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index c94922d6671..017c6f4bca9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -1,10 +1,11 @@ use crate::{ abis::{ accumulated_data::{ - CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, - PrivateToPublicAccumulatedData, PublicAccumulatedData, - PublicAccumulatedDataArrayLengths, PublicAccumulatedDataBuilder, + avm_accumulated_data::AvmAccumulatedData, CombinedAccumulatedData, + PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PrivateToPublicAccumulatedData, + PublicAccumulatedData, PublicAccumulatedDataArrayLengths, PublicAccumulatedDataBuilder, }, + avm::AvmProofData, call_context::CallContext, combined_constant_data::CombinedConstantData, enqueued_call_data::{EnqueuedCallData, Proof}, @@ -1252,6 +1253,26 @@ impl FixtureBuilder { result.public_inputs = self.to_private_to_public_kernel_circuit_public_inputs(true); result } + + fn to_avm_accumulated_data(self) -> AvmAccumulatedData { + AvmAccumulatedData { + note_hashes: self.note_hashes.storage().map(|n: ScopedNoteHash| n.note_hash.value), + nullifiers: self.nullifiers.storage().map(|n: ScopedNullifier| n.nullifier.value), + l2_to_l1_msgs: self.l2_to_l1_msgs.storage(), + unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage(), + public_data_writes: self.public_data_writes.storage(), + } + } + + fn to_avm_proof_data(self, reverted: bool) -> AvmProofData { + let mut result: AvmProofData = std::mem::zeroed(); + + result.public_inputs.reverted = reverted; + result.public_inputs.global_variables = self.global_variables; + result.public_inputs.accumulated_data = self.to_avm_accumulated_data(); + + result + } } impl Empty for FixtureBuilder { From 89233f7828942cae1e2f555ee22d44db430fd1c9 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Mon, 18 Nov 2024 14:00:28 +0000 Subject: [PATCH 11/25] fmt --- yarn-project/world-state/src/native/native_world_state.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/yarn-project/world-state/src/native/native_world_state.ts b/yarn-project/world-state/src/native/native_world_state.ts index 9db53941ffd..cf549e8f70e 100644 --- a/yarn-project/world-state/src/native/native_world_state.ts +++ b/yarn-project/world-state/src/native/native_world_state.ts @@ -12,7 +12,6 @@ import { Header, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NullifierLeaf, type NullifierLeafPreimage, @@ -166,7 +165,6 @@ export class NativeWorldStateService implements MerkleTreeDatabase { batchesOfPaddedPublicDataWrites.push( txEffect.publicDataWrites.map(write => { if (write.isEmpty()) { - console.log(txEffect); throw new Error('Public data write must not be empty when syncing'); } return new PublicDataTreeLeaf(write.leafSlot, write.value); @@ -174,8 +172,6 @@ export class NativeWorldStateService implements MerkleTreeDatabase { ); } - console.log(batchesOfPaddedPublicDataWrites); - const response = await this.instance.call(WorldStateMessageType.SYNC_BLOCK, { blockNumber: l2Block.number, blockHeaderHash: l2Block.header.hash(), From 3aad930f40a7fd0ad21fc8398e82a75700dcc5de Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 10:42:56 +0000 Subject: [PATCH 12/25] refactor rename --- .../crypto/merkle_tree/lmdb_store/lmdb_tree_store.cpp | 4 ++-- .../cpp/src/barretenberg/world_state_napi/addon.cpp | 2 +- .../cpp/src/barretenberg/world_state_napi/message.hpp | 4 ++-- yarn-project/world-state/src/native/message.ts | 2 +- yarn-project/world-state/src/native/native_world_state.ts | 6 +++--- .../world-state/src/native/native_world_state_instance.ts | 5 +---- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/lmdb_store/lmdb_tree_store.cpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/lmdb_store/lmdb_tree_store.cpp index 742f36d0395..4599df682c8 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/lmdb_store/lmdb_tree_store.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/lmdb_store/lmdb_tree_store.cpp @@ -188,7 +188,7 @@ void LMDBTreeStore::increment_node_reference_count(const fr& nodeHash, WriteTran NodePayload nodePayload; bool success = get_node_data(nodeHash, nodePayload, tx); if (!success) { - throw std::runtime_error("Failed to find node when attempting to increases reference count"); + throw std::runtime_error("Failed to find node when attempting to increase reference count"); } ++nodePayload.ref; // std::cout << "Incrementing siblng at " << nodeHash << ", to " << nodePayload.ref << std::endl; @@ -212,7 +212,7 @@ void LMDBTreeStore::decrement_node_reference_count(const fr& nodeHash, NodePaylo { bool success = get_node_data(nodeHash, nodeData, tx); if (!success) { - throw std::runtime_error("Failed to find node when attempting to increases reference count"); + throw std::runtime_error("Failed to find node when attempting to decrease reference count"); } if (--nodeData.ref == 0) { // std::cout << "Deleting node at " << nodeHash << std::endl; diff --git a/barretenberg/cpp/src/barretenberg/world_state_napi/addon.cpp b/barretenberg/cpp/src/barretenberg/world_state_napi/addon.cpp index ce0e8c301a4..f5b1ce4b129 100644 --- a/barretenberg/cpp/src/barretenberg/world_state_napi/addon.cpp +++ b/barretenberg/cpp/src/barretenberg/world_state_napi/addon.cpp @@ -560,7 +560,7 @@ bool WorldStateAddon::sync_block(msgpack::object& obj, msgpack::sbuffer& buf) request.value.paddedNoteHashes, request.value.paddedL1ToL2Messages, request.value.paddedNullifiers, - request.value.batchesOfPaddedPublicDataWrites); + request.value.batchesOfPublicDataWrites); MsgHeader header(request.header.messageId); messaging::TypedMessage resp_msg(WorldStateMessageType::SYNC_BLOCK, header, { status }); diff --git a/barretenberg/cpp/src/barretenberg/world_state_napi/message.hpp b/barretenberg/cpp/src/barretenberg/world_state_napi/message.hpp index 4d686c9362e..23f293fbebe 100644 --- a/barretenberg/cpp/src/barretenberg/world_state_napi/message.hpp +++ b/barretenberg/cpp/src/barretenberg/world_state_napi/message.hpp @@ -181,7 +181,7 @@ struct SyncBlockRequest { bb::fr blockHeaderHash; std::vector paddedNoteHashes, paddedL1ToL2Messages; std::vector paddedNullifiers; - std::vector> batchesOfPaddedPublicDataWrites; + std::vector> batchesOfPublicDataWrites; MSGPACK_FIELDS(blockNumber, blockStateRef, @@ -189,7 +189,7 @@ struct SyncBlockRequest { paddedNoteHashes, paddedL1ToL2Messages, paddedNullifiers, - batchesOfPaddedPublicDataWrites); + batchesOfPublicDataWrites); }; } // namespace bb::world_state diff --git a/yarn-project/world-state/src/native/message.ts b/yarn-project/world-state/src/native/message.ts index 51b6163c223..3ee22e1aed3 100644 --- a/yarn-project/world-state/src/native/message.ts +++ b/yarn-project/world-state/src/native/message.ts @@ -400,7 +400,7 @@ interface SyncBlockRequest { paddedNoteHashes: readonly SerializedLeafValue[]; paddedL1ToL2Messages: readonly SerializedLeafValue[]; paddedNullifiers: readonly SerializedLeafValue[]; - batchesOfPaddedPublicDataWrites: readonly SerializedLeafValue[][]; + batchesOfPublicDataWrites: readonly SerializedLeafValue[][]; } interface CreateForkRequest { diff --git a/yarn-project/world-state/src/native/native_world_state.ts b/yarn-project/world-state/src/native/native_world_state.ts index cf549e8f70e..cb701d70c0a 100644 --- a/yarn-project/world-state/src/native/native_world_state.ts +++ b/yarn-project/world-state/src/native/native_world_state.ts @@ -160,9 +160,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase { .map(nullifier => new NullifierLeaf(nullifier)); // We insert the public data tree leaves with one batch per tx to avoid updating the same key twice - const batchesOfPaddedPublicDataWrites: PublicDataTreeLeaf[][] = []; + const batchesOfPublicDataWrites: PublicDataTreeLeaf[][] = []; for (const txEffect of paddedTxEffects) { - batchesOfPaddedPublicDataWrites.push( + batchesOfPublicDataWrites.push( txEffect.publicDataWrites.map(write => { if (write.isEmpty()) { throw new Error('Public data write must not be empty when syncing'); @@ -178,7 +178,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase { paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf), paddedNoteHashes: paddedNoteHashes.map(serializeLeaf), paddedNullifiers: paddedNullifiers.map(serializeLeaf), - batchesOfPaddedPublicDataWrites: batchesOfPaddedPublicDataWrites.map(batch => batch.map(serializeLeaf)), + batchesOfPublicDataWrites: batchesOfPublicDataWrites.map(batch => batch.map(serializeLeaf)), blockStateRef: blockStateReference(l2Block.header.state), }); return sanitiseFullStatus(response); diff --git a/yarn-project/world-state/src/native/native_world_state_instance.ts b/yarn-project/world-state/src/native/native_world_state_instance.ts index 01f6871c0b4..b4635735ec0 100644 --- a/yarn-project/world-state/src/native/native_world_state_instance.ts +++ b/yarn-project/world-state/src/native/native_world_state_instance.ts @@ -187,10 +187,7 @@ export class NativeWorldState implements NativeWorldStateInstance { data['notesCount'] = body.paddedNoteHashes.length; data['nullifiersCount'] = body.paddedNullifiers.length; data['l1ToL2MessagesCount'] = body.paddedL1ToL2Messages.length; - data['publicDataWritesCount'] = body.batchesOfPaddedPublicDataWrites.reduce( - (acc, batch) => acc + batch.length, - 0, - ); + data['publicDataWritesCount'] = body.batchesOfPublicDataWrites.reduce((acc, batch) => acc + batch.length, 0); } this.log.debug(`Calling messageId=${messageId} ${WorldStateMessageType[messageType]} with ${fmtLogData(data)}`); From 54f7bb56d5f3c9e77f40bf3e3cc4e379f4fc162d Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 10:51:22 +0000 Subject: [PATCH 13/25] fix commit as a block --- .../node_store/cached_content_addressed_tree_store.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp index 6e93663ff94..95d60639a72 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp @@ -656,6 +656,9 @@ void ContentAddressedCachedTreeStore::commit(TreeMeta& finalMeta, // std::cout << "Persisting data for block " << uncommittedMeta.unfinalisedBlockHeight + 1 << std::endl; persist_leaf_indices(*tx); persist_leaf_keys(uncommittedMeta.committedSize, *tx); + } + // If we are commiting a block, we need to persist the root, since the new block "references" this root + if (dataPresent || asBlock) { persist_node(std::optional(uncommittedMeta.root), 0, *tx); } if (asBlock) { From c5fd6b2ddeb8c0ee2eeb5975cebaf4f756695a3c Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 11:58:59 +0000 Subject: [PATCH 14/25] fix test --- .../node_store/cached_content_addressed_tree_store.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp index 95d60639a72..f129e9a6536 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp @@ -649,6 +649,10 @@ void ContentAddressedCachedTreeStore::commit(TreeMeta& finalMeta, hydrate_indices_from_persisted_store(*tx); } } + std::cout << "Tree " << uncommittedMeta.name << " commiting metadata " << uncommittedMeta << std::endl; + std::cout << "Tree " << uncommittedMeta.name << " old metadata " << committedMeta << std::endl; + std::cout << "Tree " << uncommittedMeta.name << " data present? " << dataPresent << std::endl; + { WriteTransactionPtr tx = create_write_transaction(); try { @@ -658,7 +662,11 @@ void ContentAddressedCachedTreeStore::commit(TreeMeta& finalMeta, persist_leaf_keys(uncommittedMeta.committedSize, *tx); } // If we are commiting a block, we need to persist the root, since the new block "references" this root - if (dataPresent || asBlock) { + // However, if the root is the empty root we can't persist it, since it's not a real node + // We are abusing the trees in some tests, trying to add empty blocks to initial empty trees + // That is not expected behavior since the unwind operation will fail trying to decrease refcount + // for the empty root, which doesn't exist. + if (dataPresent || (asBlock && uncommittedMeta.size > 0)) { persist_node(std::optional(uncommittedMeta.root), 0, *tx); } if (asBlock) { From 06c09350561630da043c3b1c24e74a80582f0554 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 11:59:20 +0000 Subject: [PATCH 15/25] remove prints --- .../node_store/cached_content_addressed_tree_store.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp index f129e9a6536..ce4ebb476ee 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp @@ -649,10 +649,6 @@ void ContentAddressedCachedTreeStore::commit(TreeMeta& finalMeta, hydrate_indices_from_persisted_store(*tx); } } - std::cout << "Tree " << uncommittedMeta.name << " commiting metadata " << uncommittedMeta << std::endl; - std::cout << "Tree " << uncommittedMeta.name << " old metadata " << committedMeta << std::endl; - std::cout << "Tree " << uncommittedMeta.name << " data present? " << dataPresent << std::endl; - { WriteTransactionPtr tx = create_write_transaction(); try { From c3950dc942431cd655941999138d0303e1621be9 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 12:01:46 +0000 Subject: [PATCH 16/25] update comments --- .../crates/rollup-lib/src/base/public_base_rollup.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index a8f3af87d10..38fd26afd77 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -329,9 +329,9 @@ impl PublicBaseRollupInputs { all_update_requests } - // Deducts the tx_fee from the FeeJuice balance of the fee_payer. If there is already a PublicDataUpdateRequest + // Deducts the tx_fee from the FeeJuice balance of the fee_payer. If there is already a PublicDataWrite // in this tx for their balance (because they issued a 'claim' to increase their balance by bridging from L1), - // update it by subtracting the tx_fee. Otherwise, build a new PublicDataUpdateRequest to subtract the tx_fee + // update it by subtracting the tx_fee. Otherwise, build a new PublicDataWrite to subtract the tx_fee // from the balance of the fee_payer, using the fee_payer_fee_juice_balance_read_hint to read the current balance. // Returns the data update request that subtracts the tx_fee from the fee_payer's balance, and the index where it // should be inserted in the public data update requests array. From ab777479b196fc87dcb529af3e1494c15c4a395b Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 12:28:19 +0000 Subject: [PATCH 17/25] fix merge --- .../node_store/cached_content_addressed_tree_store.hpp | 6 ++++++ .../crates/rollup-lib/src/base/private_base_rollup.nr | 1 - .../crates/rollup-lib/src/base/public_base_rollup.nr | 1 - .../crates/types/src/tests/fixture_builder.nr | 5 +++-- yarn-project/circuits.js/src/tests/factories.ts | 3 +++ .../noir-protocol-circuits-types/src/type_conversion.ts | 2 -- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp index ce4ebb476ee..72c4ec34cf3 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp @@ -642,6 +642,12 @@ void ContentAddressedCachedTreeStore::commit(TreeMeta& finalMeta, get_meta(uncommittedMeta, *tx, true); get_meta(committedMeta, *tx, false); + // if the meta datas are different, we have uncommitted data + bool metaToCommit = committedMeta != uncommittedMeta; + if (!metaToCommit && !asBlock) { + return; + } + auto currentRootIter = nodes_.find(uncommittedMeta.root); dataPresent = currentRootIter != nodes_.end(); if (dataPresent) { diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 0b05ff8c260..2af209ee2a1 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -419,7 +419,6 @@ mod tests { PublicDataHint { leaf_slot: leaf_preimage.slot, value: leaf_preimage.value, - override_counter: 0, membership_witness, leaf_preimage, } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 38fd26afd77..104b66c79a7 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -585,7 +585,6 @@ mod tests { PublicDataHint { leaf_slot: leaf_preimage.slot, value: leaf_preimage.value, - override_counter: 0, membership_witness, leaf_preimage, } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 4531c308bfd..75570ebd3e8 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -1,8 +1,8 @@ use crate::{ abis::{ accumulated_data::{ - CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, - PrivateToPublicAccumulatedData, + avm_accumulated_data::AvmAccumulatedData, CombinedAccumulatedData, + PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PrivateToPublicAccumulatedData, }, avm::AvmProofData, call_context::CallContext, @@ -27,6 +27,7 @@ use crate::{ public_data_write::PublicDataWrite, read_request::{ReadRequest, ScopedReadRequest}, side_effect::Counted, + tube::{PrivateTubeData, PublicTubeData}, tx_constant_data::TxConstantData, validation_requests::{ KeyValidationRequest, KeyValidationRequestAndGenerator, PrivateValidationRequests, diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 2e0e48ecdac..3a2da20f3e9 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -138,6 +138,7 @@ import { AvmPublicDataReadTreeHint, AvmPublicDataWriteTreeHint, CountedPublicCallRequest, + PrivateBaseRollupHints, PrivateBaseRollupInputs, PrivateBaseStateDiffHints, PrivateToAvmAccumulatedData, @@ -145,7 +146,9 @@ import { PrivateToPublicAccumulatedData, PrivateToPublicKernelCircuitPublicInputs, PrivateTubeData, + PublicBaseRollupHints, PublicBaseRollupInputs, + PublicBaseStateDiffHints, PublicDataWrite, PublicTubeData, ScopedL2ToL1Message, 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 164f0dc8369..27391eb641f 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -97,7 +97,6 @@ import { type PublicBaseStateDiffHints, PublicCallRequest, type PublicDataHint, - type PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, PublicDataWrite, type PublicKeys, @@ -212,7 +211,6 @@ import type { PublicBaseStateDiffHints as PublicBaseStateDiffHintsNoir, PublicCallRequest as PublicCallRequestNoir, PublicDataHint as PublicDataHintNoir, - PublicDataTreeLeaf as PublicDataTreeLeafNoir, PublicDataTreeLeafPreimage as PublicDataTreeLeafPreimageNoir, PublicDataWrite as PublicDataWriteNoir, PublicKeys as PublicKeysNoir, From 89addc3bb74f3e434dcc667b5762f3d7704588e5 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 13:17:21 +0000 Subject: [PATCH 18/25] remove padding writes --- yarn-project/prover-node/src/job/epoch-proving-job.ts | 9 ++++----- yarn-project/simulator/src/public/public_processor.ts | 7 +------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/yarn-project/prover-node/src/job/epoch-proving-job.ts b/yarn-project/prover-node/src/job/epoch-proving-job.ts index bed463abef8..66f85548c01 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job.ts @@ -201,11 +201,10 @@ export class EpochProvingJob { emptyKernelOutput.end.nullifiers.map(n => n.toBuffer()), NULLIFIER_SUBTREE_HEIGHT, ); - const allPublicDataWrites = padArrayEnd( - emptyKernelOutput.end.publicDataWrites.map(({ leafSlot, value }) => new PublicDataTreeLeaf(leafSlot, value)), - PublicDataTreeLeaf.empty(), - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); + const allPublicDataWrites = emptyKernelOutput.end.publicDataWrites + .filter(write => !write.isEmpty()) + .map(({ leafSlot, value }) => new PublicDataTreeLeaf(leafSlot, value)); + await this.db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, allPublicDataWrites.map(x => x.toBuffer()), diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index 421b9edd14e..1b18ffc5754 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -184,14 +184,9 @@ export class PublicProcessor { } } - const allPublicDataWrites = padArrayEnd( - processedTx.txEffect.publicDataWrites, - PublicDataWrite.empty(), - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); await this.db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - allPublicDataWrites.map(x => x.toBuffer()), + processedTx.txEffect.publicDataWrites.map(x => x.toBuffer()), PUBLIC_DATA_SUBTREE_HEIGHT, ); result.push(processedTx); From 2f7ac68b979c92a195d5edeea13e4ba174a9a2c9 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 13:26:08 +0000 Subject: [PATCH 19/25] remove subtree height usage --- yarn-project/prover-client/src/mocks/fixtures.ts | 11 ++--------- yarn-project/prover-node/src/job/epoch-proving-job.ts | 11 ++--------- yarn-project/simulator/src/public/public_processor.ts | 4 +--- yarn-project/txe/src/oracle/txe_oracle.ts | 3 +-- yarn-project/txe/src/txe_service/txe_service.ts | 3 +-- yarn-project/txe/src/util/txe_world_state_db.ts | 3 +-- 6 files changed, 8 insertions(+), 27 deletions(-) diff --git a/yarn-project/prover-client/src/mocks/fixtures.ts b/yarn-project/prover-client/src/mocks/fixtures.ts index a3b507b13b8..34b7cee5935 100644 --- a/yarn-project/prover-client/src/mocks/fixtures.ts +++ b/yarn-project/prover-client/src/mocks/fixtures.ts @@ -13,10 +13,7 @@ import { GlobalVariables, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NULLIFIER_TREE_HEIGHT, - PUBLIC_DATA_SUBTREE_HEIGHT, - PublicDataWrite, } from '@aztec/circuits.js'; import { padArrayEnd } from '@aztec/foundation/collection'; import { randomBytes } from '@aztec/foundation/crypto'; @@ -114,12 +111,8 @@ export const updateExpectedTreesFromTxs = async (db: MerkleTreeWriteOperations, for (const tx of txs) { await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - padArrayEnd( - tx.txEffect.publicDataWrites, - PublicDataWrite.empty(), - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ).map(write => write.toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, + tx.txEffect.publicDataWrites.map(write => write.toBuffer()), + 0, ); } }; diff --git a/yarn-project/prover-node/src/job/epoch-proving-job.ts b/yarn-project/prover-node/src/job/epoch-proving-job.ts index 66f85548c01..c50e6682be4 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job.ts @@ -12,14 +12,7 @@ import { type Tx, type TxHash, } from '@aztec/circuit-types'; -import { - KernelCircuitPublicInputs, - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - NULLIFIER_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_HEIGHT, - PublicDataTreeLeaf, -} from '@aztec/circuits.js'; -import { padArrayEnd } from '@aztec/foundation/collection'; +import { KernelCircuitPublicInputs, NULLIFIER_SUBTREE_HEIGHT, PublicDataTreeLeaf } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; import { Timer } from '@aztec/foundation/timer'; @@ -208,7 +201,7 @@ export class EpochProvingJob { await this.db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, allPublicDataWrites.map(x => x.toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, + 0, ); } } diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index 1b18ffc5754..b8a0b1ac49a 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -20,9 +20,7 @@ import { type Header, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_HEIGHT, PublicDataWrite, } from '@aztec/circuits.js'; import { padArrayEnd } from '@aztec/foundation/collection'; @@ -187,7 +185,7 @@ export class PublicProcessor { await this.db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, processedTx.txEffect.publicDataWrites.map(x => x.toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, + 0, ); result.push(processedTx); returns = returns.concat(returnValues ?? []); diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index b6c0f55b7ec..de3ec11f1fc 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -27,7 +27,6 @@ import { type NULLIFIER_TREE_HEIGHT, type NullifierLeafPreimage, PRIVATE_CONTEXT_INPUTS_LENGTH, - PUBLIC_DATA_SUBTREE_HEIGHT, type PUBLIC_DATA_TREE_HEIGHT, PUBLIC_DISPATCH_SELECTOR, PrivateContextInputs, @@ -511,7 +510,7 @@ export class TXE implements TypedOracle { await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, publicDataWrites.map(write => write.toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, + 0, ); return publicDataWrites.map(write => write.value); } diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 75c7f5b5a20..6d073db0d7e 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -4,7 +4,6 @@ import { Fr, FunctionSelector, Header, - PUBLIC_DATA_SUBTREE_HEIGHT, PublicDataTreeLeaf, PublicKeys, computePartialAddress, @@ -157,7 +156,7 @@ export class TXEService { await db.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, publicDataWrites.map(write => write.toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, + 0, ); return toForeignCallResult([toArray(publicDataWrites.map(write => write.value))]); } diff --git a/yarn-project/txe/src/util/txe_world_state_db.ts b/yarn-project/txe/src/util/txe_world_state_db.ts index f5545c6f8ba..d1a2b6f1737 100644 --- a/yarn-project/txe/src/util/txe_world_state_db.ts +++ b/yarn-project/txe/src/util/txe_world_state_db.ts @@ -3,7 +3,6 @@ import { type AztecAddress, type ContractDataSource, Fr, - PUBLIC_DATA_SUBTREE_HEIGHT, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; @@ -35,7 +34,7 @@ export class TXEWorldStateDB extends WorldStateDB { await this.merkleDb.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, [new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(contract, slot), newValue).toBuffer()], - PUBLIC_DATA_SUBTREE_HEIGHT, + 0, ); return newValue.toBigInt(); } From 993f05c8dd02a81b735b548f44af895c811f13c1 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 13:31:57 +0000 Subject: [PATCH 20/25] remove old constants --- l1-contracts/src/core/libraries/ConstantsGen.sol | 1 - .../crates/rollup-lib/src/base/private_base_rollup.nr | 9 +++------ .../crates/rollup-lib/src/base/public_base_rollup.nr | 5 ++--- .../noir-protocol-circuits/crates/types/src/constants.nr | 3 +-- yarn-project/circuits.js/src/constants.gen.ts | 1 - 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 8334c595208..4aea0600b23 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -52,7 +52,6 @@ library Constants { uint256 internal constant L1_TO_L2_MSG_SUBTREE_HEIGHT = 4; uint256 internal constant NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH = 34; uint256 internal constant NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH = 34; - uint256 internal constant PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 34; uint256 internal constant L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 35; uint256 internal constant MAX_NOTE_HASHES_PER_TX = 64; uint256 internal constant MAX_NULLIFIERS_PER_TX = 64; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 2af209ee2a1..97ca4b8c38e 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -283,17 +283,14 @@ mod tests { abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, gas::Gas, gas_fees::GasFees, kernel_circuit_public_inputs::KernelCircuitPublicInputs, - nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, + nullifier_leaf_preimage::NullifierLeafPreimage, }, address::{AztecAddress, EthAddress}, constants::{ ARCHIVE_HEIGHT, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NOTE_HASH_TREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, - PRIVATE_KERNEL_EMPTY_INDEX, PRIVATE_KERNEL_TAIL_INDEX, - PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, + PRIVATE_KERNEL_EMPTY_INDEX, PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, }, data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, hash::silo_l2_to_l1_message, @@ -405,7 +402,7 @@ mod tests { fn build_fee_payer_fee_juice_balance_read_hint( self, - start_public_data_tree: NonEmptyMerkleTree, + start_public_data_tree: NonEmptyMerkleTree, ) -> PublicDataHint { self.fee_payer_fee_juice_balance_pre_existing_public_data_index.map_or( PublicDataHint::empty(), diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 104b66c79a7..056a760cd6c 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -439,9 +439,8 @@ mod tests { MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NOTE_HASH_TREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, - PRIVATE_KERNEL_EMPTY_INDEX, PRIVATE_KERNEL_TAIL_INDEX, - PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, + PRIVATE_KERNEL_EMPTY_INDEX, PROTOCOL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, + PUBLIC_DATA_TREE_HEIGHT, TUBE_VK_INDEX, }, data::{public_data_hint::PublicDataHint, PublicDataTreeLeaf, PublicDataTreeLeafPreimage}, hash::silo_l2_to_l1_message, 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 9d875fcf4fe..fcf4127112f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -62,14 +62,13 @@ pub global ARCHIVE_TREE_ID = 4; // SUB-TREES RELATED CONSTANTS pub global NOTE_HASH_SUBTREE_HEIGHT: u32 = 6; pub global NULLIFIER_SUBTREE_HEIGHT: u32 = 6; +// Deprecated: to be removed after removal of legacy ts trees pub global PUBLIC_DATA_SUBTREE_HEIGHT: u32 = 6; pub global L1_TO_L2_MSG_SUBTREE_HEIGHT: u32 = 4; pub global NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH: u32 = NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT; pub global NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH: u32 = NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT; -pub global PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH: u32 = - PUBLIC_DATA_TREE_HEIGHT - PUBLIC_DATA_SUBTREE_HEIGHT; pub global L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH: u32 = L1_TO_L2_MSG_TREE_HEIGHT - L1_TO_L2_MSG_SUBTREE_HEIGHT; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index d315587a46f..ff6911cf4f3 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -38,7 +38,6 @@ export const PUBLIC_DATA_SUBTREE_HEIGHT = 6; export const L1_TO_L2_MSG_SUBTREE_HEIGHT = 4; export const NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH = 34; export const NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH = 34; -export const PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 34; export const L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 35; export const MAX_NOTE_HASHES_PER_TX = 64; export const MAX_NULLIFIERS_PER_TX = 64; From 27ea93f2d9a1f0cd009a199a72568bdd46bd4616 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 14:48:30 +0000 Subject: [PATCH 21/25] fix units --- .../rollup-lib/src/base/components/archive.nr | 24 ++++++++++++++ .../rollup-lib/src/base/components/mod.nr | 5 +-- .../src/base/private_base_rollup.nr | 32 ++++--------------- .../rollup-lib/src/base/public_base_rollup.nr | 32 ++++--------------- yarn-project/world-state/src/test/utils.ts | 10 ++---- 5 files changed, 43 insertions(+), 60 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/archive.nr diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/archive.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/archive.nr new file mode 100644 index 00000000000..e97541be158 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/archive.nr @@ -0,0 +1,24 @@ +use types::{ + constants::ARCHIVE_HEIGHT, + header::Header, + merkle_tree::membership::{assert_check_membership, MembershipWitness}, +}; + +// Check that the block header used is a member of the blocks tree --> since the block header +// contains roots of all the trees this is sufficient to verify that the tree roots used by kernels are correct +pub(crate) fn perform_archive_membership_check( + archive_root: Field, + previous_block_hash_witness: MembershipWitness, + header: Header, +) { + // Rebuild the block hash + let previous_block_hash = header.hash(); + + // Now check that the previous block hash is in the blocks tree from the beginning of the rollup + assert_check_membership( + previous_block_hash, + previous_block_hash_witness.leaf_index, + previous_block_hash_witness.sibling_path, + archive_root, + ); +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr index 75763a12f58..16d3eb10f9a 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr @@ -1,3 +1,4 @@ -pub(crate) mod public_data_tree; -pub(crate) mod nullifier_tree; +pub(crate) mod archive; pub(crate) mod fees; +pub(crate) mod nullifier_tree; +pub(crate) mod public_data_tree; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 97ca4b8c38e..2505aa736a5 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -5,6 +5,7 @@ use crate::{ }, base::{ components::{ + archive::perform_archive_membership_check, fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, }, @@ -25,8 +26,7 @@ use dep::types::{ data::{hash::compute_public_data_tree_value, public_data_hint::PublicDataHint}, hash::silo_l2_to_l1_message, merkle_tree::{ - append_only_tree, assert_check_membership, calculate_empty_tree_root, - calculate_subtree_root, MembershipWitness, + append_only_tree, calculate_empty_tree_root, calculate_subtree_root, MembershipWitness, }, messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, @@ -151,7 +151,11 @@ impl PrivateBaseRollupInputs { ); // Perform membership checks that the notes provided exist within the historical trees data - self.perform_archive_membership_checks(); + perform_archive_membership_check( + self.constants.last_archive.root, + self.archive_root_membership_witness, + self.tube_data.public_inputs.constants.historical_header, + ); BaseOrMergeRollupPublicInputs { rollup_type: BASE_ROLLUP_TYPE, @@ -242,28 +246,6 @@ impl PrivateBaseRollupInputs { self.state_diff_hints.fee_write_sibling_path, ) } - - // Check that the block header used by each kernel is a member of the blocks tree --> since the block header - // contains roots of all the trees this is sufficient to verify that the tree roots used by kernels are correct - fn perform_archive_membership_checks(self) { - // For each of the block header (their block hashes), we need to do an inclusion proof - // against the blocks tree root from the beginning of a rollup provided in the rollup constants - let archive_root = self.constants.last_archive.root; - - // Rebuild the block hash - let header = self.tube_data.public_inputs.constants.historical_header; - let previous_block_hash = header.hash(); - - let previous_block_hash_witness = self.archive_root_membership_witness; - - // Now check that the previous block hash is in the blocks tree from the beginning of the rollup - assert_check_membership( - previous_block_hash, - previous_block_hash_witness.leaf_index, - previous_block_hash_witness.sibling_path, - archive_root, - ); - } } mod tests { diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 056a760cd6c..34560a74c12 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -5,6 +5,7 @@ use crate::{ }, base::{ components::{ + archive::perform_archive_membership_check, fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, }, @@ -31,8 +32,7 @@ use dep::types::{ data::{hash::compute_public_data_tree_value, public_data_hint::PublicDataHint}, hash::silo_l2_to_l1_message, merkle_tree::{ - append_only_tree, assert_check_membership, calculate_empty_tree_root, - calculate_subtree_root, MembershipWitness, + append_only_tree, calculate_empty_tree_root, calculate_subtree_root, MembershipWitness, }, messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, @@ -231,7 +231,11 @@ impl PublicBaseRollupInputs { ); // Perform membership checks that the notes provided exist within the historical trees data - self.perform_archive_membership_checks(public_inputs); + perform_archive_membership_check( + self.constants.last_archive.root, + self.archive_root_membership_witness, + public_inputs.constants.historical_header, + ); BaseOrMergeRollupPublicInputs { rollup_type: BASE_ROLLUP_TYPE, @@ -391,28 +395,6 @@ impl PublicBaseRollupInputs { (PublicDataWrite::empty(), MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX) } } - - // Check that the block header used by each kernel is a member of the blocks tree --> since the block header - // contains roots of all the trees this is sufficient to verify that the tree roots used by kernels are correct - fn perform_archive_membership_checks(self, public_inputs: KernelCircuitPublicInputs) { - // For each of the block header (their block hashes), we need to do an inclusion proof - // against the blocks tree root from the beginning of a rollup provided in the rollup constants - let archive_root = self.constants.last_archive.root; - - // Rebuild the block hash - let header = public_inputs.constants.historical_header; - let previous_block_hash = header.hash(); - - let previous_block_hash_witness = self.archive_root_membership_witness; - - // Now check that the previous block hash is in the blocks tree from the beginning of the rollup - assert_check_membership( - previous_block_hash, - previous_block_hash_witness.leaf_index, - previous_block_hash_witness.sibling_path, - archive_root, - ); - } } mod tests { diff --git a/yarn-project/world-state/src/test/utils.ts b/yarn-project/world-state/src/test/utils.ts index e6148637fef..ca22d3de193 100644 --- a/yarn-project/world-state/src/test/utils.ts +++ b/yarn-project/world-state/src/test/utils.ts @@ -45,16 +45,10 @@ export async function mockBlock(blockNum: number, size: number, fork: MerkleTree { // We insert the public data tree leaves with one batch per tx to avoid updating the same key twice for (const txEffect of paddedTxEffects) { - const publicDataWrites = padArrayEnd( - txEffect.publicDataWrites, - PublicDataWrite.empty(), - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - ); - await fork.batchInsert( MerkleTreeId.PUBLIC_DATA_TREE, - publicDataWrites.map(write => write.toBuffer()), - PUBLIC_DATA_SUBTREE_HEIGHT, + txEffect.publicDataWrites.map(write => write.toBuffer()), + 0, ); const nullifiersPadded = padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX); From 2ed3706622b4e2d83cb4c62833d1380a54f8b16f Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 15:21:28 +0000 Subject: [PATCH 22/25] refactor public base --- .../rollup-lib/src/base/public_base_rollup.nr | 114 ++++++++---------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 34560a74c12..5664b589df4 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -54,10 +54,9 @@ pub struct PublicBaseRollupInputs { } impl PublicBaseRollupInputs { - fn generate_kernel_circuit_public_inputs(self) -> KernelCircuitPublicInputs { + fn generate_combined_accumulated_data(self, reverted: bool) -> CombinedAccumulatedData { let from_private = self.tube_data.public_inputs; let from_public = self.avm_proof_data.public_inputs; - let reverted = from_public.reverted; let note_encrypted_logs_hashes = if reverted { from_private.non_revertible_accumulated_data.note_encrypted_logs_hashes @@ -93,7 +92,8 @@ impl PublicBaseRollupInputs { .accumulated_data .unencrypted_logs_hashes .fold(0, |len, l: ScopedLogHash| len + l.log_hash.length); - let end = CombinedAccumulatedData { + + CombinedAccumulatedData { note_hashes: from_public.accumulated_data.note_hashes, nullifiers: from_public.accumulated_data.nullifiers, l2_to_l1_msgs: from_public.accumulated_data.l2_to_l1_msgs, @@ -106,27 +106,6 @@ impl PublicBaseRollupInputs { unencrypted_log_preimages_length, contract_class_log_preimages_length, public_data_writes: from_public.accumulated_data.public_data_writes, - }; - - let constants = - CombinedConstantData::combine(from_private.constants, from_public.global_variables); - - let start_state = PartialStateReference { - note_hash_tree: from_public.start_tree_snapshots.note_hash_tree, - nullifier_tree: from_public.start_tree_snapshots.nullifier_tree, - public_data_tree: from_public.start_tree_snapshots.public_data_tree, - }; - - let revert_code = if from_public.reverted { 1 } else { 0 }; - - KernelCircuitPublicInputs { - constants, - rollup_validation_requests: from_private.rollup_validation_requests, - end, - start_state, - revert_code, - gas_used: from_public.end_gas_used, - fee_payer: from_private.fee_payer, } } @@ -143,39 +122,46 @@ impl PublicBaseRollupInputs { // self.avm_proof_data.vk_data.validate_in_vk_tree([AVM_VK_INDEX]); // } // TODO: Validate tube_data.public_inputs vs avm_proof_data.public_inputs - // TODO: Use both public avm and private inputs instead of merging them into one KernelCircuitPublicInputs - let public_inputs = self.generate_kernel_circuit_public_inputs(); + + let reverted = self.avm_proof_data.public_inputs.reverted; + let revert_code = if reverted { 1 } else { 0 }; + + let constants = CombinedConstantData::combine( + self.tube_data.public_inputs.constants, + self.avm_proof_data.public_inputs.global_variables, + ); + + let combined_accumulated_data = self.generate_combined_accumulated_data(reverted); // Verify the kernel chain_id and versions assert( - public_inputs.constants.tx_context.chain_id == self.constants.global_variables.chain_id, + constants.tx_context.chain_id == self.constants.global_variables.chain_id, "kernel chain_id does not match the rollup chain_id", ); assert( - public_inputs.constants.tx_context.version == self.constants.global_variables.version, + constants.tx_context.version == self.constants.global_variables.version, "kernel version does not match the rollup version", ); assert( - public_inputs.constants.vk_tree_root == self.constants.vk_tree_root, + constants.vk_tree_root == self.constants.vk_tree_root, "kernel vk_tree_root does not match the rollup vk_tree_root", ); assert( - public_inputs.constants.protocol_contract_tree_root - == self.constants.protocol_contract_tree_root, + constants.protocol_contract_tree_root == self.constants.protocol_contract_tree_root, "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", ); // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? assert( - public_inputs.constants.global_variables.is_empty() - | (public_inputs.constants.global_variables == self.constants.global_variables), + constants.global_variables.is_empty() + | (constants.global_variables == self.constants.global_variables), "kernel global variables do not match the rollup global variables", ); - self.validate_kernel_start_state(public_inputs); + self.validate_kernel_start_state(); - let rollup_validation_requests = public_inputs.rollup_validation_requests; + let rollup_validation_requests = self.tube_data.public_inputs.rollup_validation_requests; // Verify the max block number // TODO #5345: why is block_number a Field and not u32? @@ -187,7 +173,8 @@ impl PublicBaseRollupInputs { ); } - let commitments_tree_subroot = self.calculate_commitments_subtree_root(public_inputs); + let commitments_tree_subroot = + calculate_subtree_root(combined_accumulated_data.note_hashes); let empty_commitments_subtree_root = calculate_empty_tree_root(NOTE_HASH_SUBTREE_HEIGHT); @@ -201,12 +188,12 @@ impl PublicBaseRollupInputs { // Insert nullifiers: let end_nullifier_tree_snapshot = - self.check_nullifier_tree_non_membership_and_insert_to_tree(public_inputs); + self.check_nullifier_tree_non_membership_and_insert_to_tree(combined_accumulated_data); // Inject protocol update requests for deducting tx_fee from fee_payer's balance let all_public_data_update_requests = self.calculate_all_public_data_update_requests( self.avm_proof_data.public_inputs.transaction_fee, - public_inputs, + combined_accumulated_data, ); // Validate public data update requests and update public data tree @@ -214,17 +201,17 @@ impl PublicBaseRollupInputs { self.validate_and_process_public_state(all_public_data_update_requests); // Calculate the tx effects hash of the transaction - let siloed_l2_to_l1_msgs = public_inputs.end.l2_to_l1_msgs.map( + let siloed_l2_to_l1_msgs = combined_accumulated_data.l2_to_l1_msgs.map( |message: ScopedL2ToL1Message| silo_l2_to_l1_message( message, - public_inputs.constants.tx_context.version, - public_inputs.constants.tx_context.chain_id, + constants.tx_context.version, + constants.tx_context.chain_id, ), ); let out_hash = compute_kernel_out_hash(siloed_l2_to_l1_msgs); let tx_effects_hash = compute_tx_effects_hash( - public_inputs.end, - public_inputs.revert_code, + combined_accumulated_data, + revert_code, self.avm_proof_data.public_inputs.transaction_fee, all_public_data_update_requests, out_hash, @@ -234,7 +221,7 @@ impl PublicBaseRollupInputs { perform_archive_membership_check( self.constants.last_archive.root, self.archive_root_membership_witness, - public_inputs.constants.historical_header, + constants.historical_header, ); BaseOrMergeRollupPublicInputs { @@ -253,17 +240,13 @@ impl PublicBaseRollupInputs { } } - fn calculate_commitments_subtree_root(self, public_inputs: KernelCircuitPublicInputs) -> Field { - calculate_subtree_root(public_inputs.end.note_hashes) - } - fn check_nullifier_tree_non_membership_and_insert_to_tree( self, - public_inputs: KernelCircuitPublicInputs, + accumulated_data: CombinedAccumulatedData, ) -> AppendOnlyTreeSnapshot { nullifier_tree_batch_insert( self.start.nullifier_tree, - public_inputs.end.nullifiers, + accumulated_data.nullifiers, self.state_diff_hints.sorted_nullifiers, self.state_diff_hints.sorted_nullifier_indexes, self.state_diff_hints.nullifier_subtree_sibling_path, @@ -276,19 +259,24 @@ impl PublicBaseRollupInputs { calculate_subtree_root(leaves.map(|leaf: NullifierLeafPreimage| leaf.hash())) } - fn validate_kernel_start_state(self, public_inputs: KernelCircuitPublicInputs) { - let kernel_state = public_inputs.start_state; - if !is_empty(kernel_state) { + fn validate_kernel_start_state(self) { + let start_tree_snapshots = self.avm_proof_data.public_inputs.start_tree_snapshots; + let start_state = PartialStateReference { + note_hash_tree: start_tree_snapshots.note_hash_tree, + nullifier_tree: start_tree_snapshots.nullifier_tree, + public_data_tree: start_tree_snapshots.public_data_tree, + }; + if !is_empty(start_state) { assert( - kernel_state.note_hash_tree.eq(self.start.note_hash_tree), + start_state.note_hash_tree.eq(self.start.note_hash_tree), "Mismatch start state for note hash tree", ); assert( - kernel_state.nullifier_tree.eq(self.start.nullifier_tree), + start_state.nullifier_tree.eq(self.start.nullifier_tree), "Mismatch start state for nullifier tree", ); assert( - kernel_state.public_data_tree.eq(self.start.public_data_tree), + start_state.public_data_tree.eq(self.start.public_data_tree), "Mismatch start state for public data tree", ); } @@ -318,16 +306,16 @@ impl PublicBaseRollupInputs { fn calculate_all_public_data_update_requests( self, tx_fee: Field, - public_inputs: KernelCircuitPublicInputs, + accumulated_data: CombinedAccumulatedData, ) -> [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] { let mut all_update_requests: [PublicDataWrite; MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX] = [PublicDataWrite::empty(); MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX]; for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - all_update_requests[i] = public_inputs.end.public_data_writes[i]; + all_update_requests[i] = accumulated_data.public_data_writes[i]; } let (payment_update_request, payment_update_index) = - self.build_or_patch_payment_update_request(tx_fee, public_inputs); + self.build_or_patch_payment_update_request(tx_fee, accumulated_data); all_update_requests[payment_update_index] = payment_update_request; all_update_requests @@ -342,9 +330,9 @@ impl PublicBaseRollupInputs { fn build_or_patch_payment_update_request( self, tx_fee: Field, - public_inputs: KernelCircuitPublicInputs, + accumulated_data: CombinedAccumulatedData, ) -> (PublicDataWrite, u32) { - let fee_payer = public_inputs.fee_payer; + let fee_payer = self.tube_data.public_inputs.fee_payer; // TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx if !fee_payer.is_zero() { @@ -352,14 +340,14 @@ impl PublicBaseRollupInputs { let leaf_slot = compute_fee_payer_fee_juice_balance_leaf_slot(fee_payer); let existing_update_index = unsafe { find_index_hint( - public_inputs.end.public_data_writes, + accumulated_data.public_data_writes, |w: PublicDataWrite| w.leaf_slot == leaf_slot, ) }; if existing_update_index != MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { // Is there a balance update already in this tx? If so, update it and return its index. - let existing_update = public_inputs.end.public_data_writes[existing_update_index]; + let existing_update = accumulated_data.public_data_writes[existing_update_index]; assert( existing_update.leaf_slot == leaf_slot, "Wrong leaf slot for Fee Juice balance update request", From c4eb74aa8602433dc5e526211a2635aa55524a15 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 15:43:26 +0000 Subject: [PATCH 23/25] more base rollup refactor --- .../src/abis/constant_rollup_data.nr | 8 ++-- .../crates/rollup-lib/src/abis/mod.nr | 10 ++--- .../src/base/components/constants.nr | 33 ++++++++++++++ .../rollup-lib/src/base/components/mod.nr | 1 + .../src/base/components/nullifier_tree.nr | 1 - .../crates/rollup-lib/src/base/mod.nr | 4 +- .../src/base/private_base_rollup.nr | 34 +------------- .../rollup-lib/src/base/public_base_rollup.nr | 45 +++++-------------- .../crates/types/src/abis/global_variables.nr | 2 +- .../types/src/abis/public_data_write.nr | 4 +- yarn-project/world-state/src/test/utils.ts | 3 -- 11 files changed, 61 insertions(+), 84 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr index ee9c307fc48..9eab56a2092 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr @@ -7,10 +7,10 @@ use dep::types::{ pub struct ConstantRollupData { // Archive tree snapshot at the very beginning of the entire rollup. - last_archive: AppendOnlyTreeSnapshot, - vk_tree_root: Field, - protocol_contract_tree_root: Field, - global_variables: GlobalVariables, + pub last_archive: AppendOnlyTreeSnapshot, + pub vk_tree_root: Field, + pub protocol_contract_tree_root: Field, + pub global_variables: GlobalVariables, } impl Eq for ConstantRollupData { diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr index b57aeaa5fa0..716b67bf953 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr @@ -1,5 +1,5 @@ -mod constant_rollup_data; -mod base_or_merge_rollup_public_inputs; -mod block_root_or_block_merge_public_inputs; -mod previous_rollup_data; -mod previous_rollup_block_data; +pub(crate) mod constant_rollup_data; +pub(crate) mod base_or_merge_rollup_public_inputs; +pub(crate) mod block_root_or_block_merge_public_inputs; +pub(crate) mod previous_rollup_data; +pub(crate) mod previous_rollup_block_data; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr new file mode 100644 index 00000000000..0e7f459fecd --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr @@ -0,0 +1,33 @@ +use crate::abis::constant_rollup_data::ConstantRollupData; +use types::abis::combined_constant_data::CombinedConstantData; + +pub(crate) fn validate_combined_constant_data( + constants: CombinedConstantData, + rollup_constants: ConstantRollupData, +) { + // Verify the kernel chain_id and versions + assert( + constants.tx_context.chain_id == rollup_constants.global_variables.chain_id, + "kernel chain_id does not match the rollup chain_id", + ); + assert( + constants.tx_context.version == rollup_constants.global_variables.version, + "kernel version does not match the rollup version", + ); + assert( + constants.vk_tree_root == rollup_constants.vk_tree_root, + "kernel vk_tree_root does not match the rollup vk_tree_root", + ); + assert( + constants.protocol_contract_tree_root == rollup_constants.protocol_contract_tree_root, + "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", + ); + + // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. + // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? + assert( + constants.global_variables.is_empty() + | (constants.global_variables == rollup_constants.global_variables), + "kernel global variables do not match the rollup global variables", + ); +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr index 16d3eb10f9a..9e3e70f9433 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr @@ -1,4 +1,5 @@ pub(crate) mod archive; +pub(crate) mod constants; pub(crate) mod fees; pub(crate) mod nullifier_tree; pub(crate) mod public_data_tree; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr index ccda4d8c448..6f83aa7e5ee 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/nullifier_tree.nr @@ -7,7 +7,6 @@ use dep::types::{ MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, }, - data, merkle_tree::{indexed_tree, MembershipWitness}, utils::field::full_field_less_than, }; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr index d883ab9dff2..03b442b4eaa 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr @@ -1,7 +1,7 @@ -mod components; +pub(crate) mod components; +pub(crate) mod state_diff_hints; mod private_base_rollup; mod public_base_rollup; -mod state_diff_hints; pub use crate::abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs; pub use private_base_rollup::PrivateBaseRollupInputs; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index 2505aa736a5..30b20e4b88f 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -5,7 +5,7 @@ use crate::{ }, base::{ components::{ - archive::perform_archive_membership_check, + archive::perform_archive_membership_check, constants::validate_combined_constant_data, fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, }, @@ -63,37 +63,7 @@ impl PrivateBaseRollupInputs { let transaction_fee = self.compute_transaction_fee(); - // Verify the kernel chain_id and versions - assert( - self.tube_data.public_inputs.constants.tx_context.chain_id - == self.constants.global_variables.chain_id, - "kernel chain_id does not match the rollup chain_id", - ); - assert( - self.tube_data.public_inputs.constants.tx_context.version - == self.constants.global_variables.version, - "kernel version does not match the rollup version", - ); - assert( - self.tube_data.public_inputs.constants.vk_tree_root == self.constants.vk_tree_root, - "kernel vk_tree_root does not match the rollup vk_tree_root", - ); - assert( - self.tube_data.public_inputs.constants.protocol_contract_tree_root - == self.constants.protocol_contract_tree_root, - "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", - ); - - // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. - // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? - assert( - self.tube_data.public_inputs.constants.global_variables.is_empty() - | ( - self.tube_data.public_inputs.constants.global_variables - == self.constants.global_variables - ), - "kernel global variables do not match the rollup global variables", - ); + validate_combined_constant_data(self.tube_data.public_inputs.constants, self.constants); self.validate_kernel_start_state(); diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 5664b589df4..7edaf06261c 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -5,7 +5,7 @@ use crate::{ }, base::{ components::{ - archive::perform_archive_membership_check, + archive::perform_archive_membership_check, constants::validate_combined_constant_data, fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, }, @@ -124,40 +124,14 @@ impl PublicBaseRollupInputs { // TODO: Validate tube_data.public_inputs vs avm_proof_data.public_inputs let reverted = self.avm_proof_data.public_inputs.reverted; - let revert_code = if reverted { 1 } else { 0 }; - - let constants = CombinedConstantData::combine( - self.tube_data.public_inputs.constants, - self.avm_proof_data.public_inputs.global_variables, - ); let combined_accumulated_data = self.generate_combined_accumulated_data(reverted); - // Verify the kernel chain_id and versions - assert( - constants.tx_context.chain_id == self.constants.global_variables.chain_id, - "kernel chain_id does not match the rollup chain_id", - ); - assert( - constants.tx_context.version == self.constants.global_variables.version, - "kernel version does not match the rollup version", - ); - assert( - constants.vk_tree_root == self.constants.vk_tree_root, - "kernel vk_tree_root does not match the rollup vk_tree_root", - ); - assert( - constants.protocol_contract_tree_root == self.constants.protocol_contract_tree_root, - "kernel protocol_contract_tree_root does not match the rollup protocol_contract_tree_root", - ); - - // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. - // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? - assert( - constants.global_variables.is_empty() - | (constants.global_variables == self.constants.global_variables), - "kernel global variables do not match the rollup global variables", + let combined_constant_data = CombinedConstantData::combine( + self.tube_data.public_inputs.constants, + self.avm_proof_data.public_inputs.global_variables, ); + validate_combined_constant_data(combined_constant_data, self.constants); self.validate_kernel_start_state(); @@ -204,11 +178,14 @@ impl PublicBaseRollupInputs { let siloed_l2_to_l1_msgs = combined_accumulated_data.l2_to_l1_msgs.map( |message: ScopedL2ToL1Message| silo_l2_to_l1_message( message, - constants.tx_context.version, - constants.tx_context.chain_id, + combined_constant_data.tx_context.version, + combined_constant_data.tx_context.chain_id, ), ); let out_hash = compute_kernel_out_hash(siloed_l2_to_l1_msgs); + + let revert_code = if reverted { 1 } else { 0 }; + let tx_effects_hash = compute_tx_effects_hash( combined_accumulated_data, revert_code, @@ -221,7 +198,7 @@ impl PublicBaseRollupInputs { perform_archive_membership_check( self.constants.last_archive.root, self.archive_root_membership_witness, - constants.historical_header, + combined_constant_data.historical_header, ); BaseOrMergeRollupPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr index deb4de74aa2..936cd08b645 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr @@ -20,7 +20,7 @@ pub struct GlobalVariables { // docs:end:global-variables impl GlobalVariables { - fn is_empty(self) -> bool { + pub fn is_empty(self) -> bool { (self.chain_id == 0) & (self.version == 0) & (self.block_number == 0) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr index 9a1d57e262c..5f28fed80d9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_data_write.nr @@ -1,8 +1,8 @@ use crate::{constants::PUBLIC_DATA_WRITE_LENGTH, traits::{Deserialize, Empty, Serialize}}; pub struct PublicDataWrite { - leaf_slot: Field, - value: Field, + pub leaf_slot: Field, + pub value: Field, } impl Eq for PublicDataWrite { diff --git a/yarn-project/world-state/src/test/utils.ts b/yarn-project/world-state/src/test/utils.ts index ca22d3de193..4edd1888384 100644 --- a/yarn-project/world-state/src/test/utils.ts +++ b/yarn-project/world-state/src/test/utils.ts @@ -10,11 +10,8 @@ import { Fr, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, - MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - PUBLIC_DATA_SUBTREE_HEIGHT, - PublicDataWrite, } from '@aztec/circuits.js'; import { padArrayEnd } from '@aztec/foundation/collection'; From 937bb4ac37c9b63165e27fff7b941df191092c58 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 19 Nov 2024 15:52:00 +0000 Subject: [PATCH 24/25] comments --- .../src/structs/rollup/state_diff_hints.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts index a59b8d7466f..f1b2a5e986b 100644 --- a/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts +++ b/yarn-project/circuits.js/src/structs/rollup/state_diff_hints.ts @@ -49,8 +49,17 @@ export class PrivateBaseStateDiffHints { */ public nullifierSubtreeSiblingPath: Tuple, + /** + * Low leaf for the fee write in the public data tree. + */ public feeWriteLowLeafPreimage: PublicDataTreeLeafPreimage, + /** + * Membership witness for the low leaf for the fee write in the public data tree. + */ public feeWriteLowLeafMembershipWitness: MembershipWitness, + /** + * Sibling path "pointing to" where the fee write should be inserted into the public data tree. + */ public feeWriteSiblingPath: Tuple, ) {} @@ -149,16 +158,25 @@ export class PublicBaseStateDiffHints { */ public nullifierSubtreeSiblingPath: Tuple, + /** + * Preimages of the low leaves for the public data writes + */ public lowPublicDataWritesPreimages: Tuple< PublicDataTreeLeafPreimage, typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX >, + /** + * Membership witnesses for the low leaves for the public data writes + */ public lowPublicDataWritesMembershipWitnesses: Tuple< MembershipWitness, typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX >, + /** + * Sibling paths "pointing to" where the new public data writes should be inserted (one by one) into the public data tree. + */ public publicDataTreeSiblingPaths: Tuple< Tuple, typeof MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX From 9d00674191891a04e72765231aac9d47c92635c1 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Wed, 20 Nov 2024 08:21:26 +0000 Subject: [PATCH 25/25] refactor --- .../src/abis/base_or_merge_rollup_public_inputs.nr | 4 ++-- .../crates/rollup-lib/src/base/public_base_rollup.nr | 3 +-- .../types/src/abis/{avm.nr => avm_circuit_public_inputs.nr} | 0 .../noir-protocol-circuits/crates/types/src/abis/mod.nr | 2 +- .../noir-protocol-circuits/crates/types/src/abis/tube.nr | 6 +++--- .../crates/types/src/tests/fixture_builder.nr | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) rename noir-projects/noir-protocol-circuits/crates/types/src/abis/{avm.nr => avm_circuit_public_inputs.nr} (100%) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr index 4cdddc31ab8..c591c30cdfa 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr @@ -6,8 +6,8 @@ use dep::types::{ utils::reader::Reader, }; -global BASE_ROLLUP_TYPE = 0; -global MERGE_ROLLUP_TYPE = 1; +pub(crate) global BASE_ROLLUP_TYPE = 0; +pub(crate) global MERGE_ROLLUP_TYPE = 1; pub struct BaseOrMergeRollupPublicInputs { // rollup_type is either 0 (base) or 1 (merge) diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index 7edaf06261c..1fff4bb07ff 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -17,9 +17,8 @@ use dep::types::{ abis::{ accumulated_data::CombinedAccumulatedData, append_only_tree_snapshot::AppendOnlyTreeSnapshot, - avm::AvmProofData, + avm_circuit_public_inputs::AvmProofData, combined_constant_data::CombinedConstantData, - kernel_circuit_public_inputs::KernelCircuitPublicInputs, log_hash::{LogHash, ScopedLogHash}, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/avm.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/avm_circuit_public_inputs.nr similarity index 100% rename from noir-projects/noir-protocol-circuits/crates/types/src/abis/avm.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/abis/avm_circuit_public_inputs.nr diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr index 2efd5b83754..890d2d4429d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr @@ -1,5 +1,5 @@ pub mod append_only_tree_snapshot; -pub mod avm; +pub mod avm_circuit_public_inputs; pub mod contract_class_function_leaf_preimage; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr index 814a5bfdd7e..e36c2ead478 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr @@ -7,9 +7,9 @@ use crate::{ }; pub struct PublicTubeData { - public_inputs: PrivateToPublicKernelCircuitPublicInputs, - proof: TubeProof, - vk_data: VkData, + pub public_inputs: PrivateToPublicKernelCircuitPublicInputs, + pub proof: TubeProof, + pub vk_data: VkData, } impl Verifiable for PublicTubeData { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index 75570ebd3e8..94294b65c16 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -4,7 +4,7 @@ use crate::{ avm_accumulated_data::AvmAccumulatedData, CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PrivateToPublicAccumulatedData, }, - avm::AvmProofData, + avm_circuit_public_inputs::AvmProofData, call_context::CallContext, combined_constant_data::CombinedConstantData, function_data::FunctionData,