From 4e59b06cd1956d43bc44a219448603b4bcf58d27 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 16 Dec 2024 15:53:39 +0000 Subject: [PATCH 01/10] feat: check max fees per gas (#10283) Please read [contributing guidelines](CONTRIBUTING.md) and remove this line. --- .../tail_to_public_output_validator.nr | 3 +- .../base_or_merge_rollup_public_inputs.nr | 3 +- .../crates/rollup-lib/src/abis/mod.nr | 1 - .../src/base/components/constants.nr | 5 +- .../rollup-lib/src/base/components/mod.nr | 7 ++ .../components/private_tube_data_validator.nr | 28 ++++++ .../components/public_tube_data_validator.nr | 28 ++++++ .../src/base/components/validate_tube_data.nr | 12 +++ .../crates/rollup-lib/src/base/mod.nr | 1 + .../src/base/private_base_rollup.nr | 38 ++++---- .../rollup-lib/src/base/public_base_rollup.nr | 31 +++---- .../crates/rollup-lib/src/base/tests/mod.nr | 1 + .../mod.nr | 24 +++++ .../validate_with_rollup_data.nr | 27 ++++++ .../src/abis/avm_circuit_public_inputs.nr | 2 +- .../src/abis/constant_rollup_data.nr | 2 +- .../crates/types/src/abis/mod.nr | 1 + .../abis/private_kernel/private_call_data.nr | 2 +- .../crates/types/src/tests/fixture_builder.nr | 87 +++++++++++-------- .../src/tx/validator/empty_validator.ts | 4 +- .../src/tx/validator/tx_validator.ts | 2 +- .../aggregate_tx_validator.test.ts | 49 ++++++++--- .../tx_validator/aggregate_tx_validator.ts | 8 +- .../src/sequencer/sequencer.test.ts | 42 +++++++++ .../src/tx_validator/gas_validator.test.ts | 75 ++++++++++------ .../src/tx_validator/gas_validator.ts | 38 ++++++-- .../src/tx_validator/tx_validator_factory.ts | 7 +- 27 files changed, 398 insertions(+), 130 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr rename noir-projects/noir-protocol-circuits/crates/{rollup-lib => types}/src/abis/constant_rollup_data.nr (99%) diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr index bbf93064ee1..77f00bbd3f3 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr @@ -9,9 +9,8 @@ use dep::types::{ log_hash::ScopedLogHash, note_hash::ScopedNoteHash, nullifier::ScopedNullifier, - private_log::{PrivateLog, PrivateLogData}, public_call_request::PublicCallRequest, - side_effect::{Counted, scoped::Scoped}, + side_effect::Counted, }, messaging::l2_to_l1_message::ScopedL2ToL1Message, utils::arrays::{ 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 7dc56ab336a..155471c7962 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 @@ -1,6 +1,5 @@ -use crate::abis::constant_rollup_data::ConstantRollupData; use dep::types::{ - abis::sponge_blob::SpongeBlob, + abis::{constant_rollup_data::ConstantRollupData, sponge_blob::SpongeBlob}, constants::BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH, partial_state_reference::PartialStateReference, traits::{Deserialize, Empty, Serialize}, 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 716b67bf953..0bcaaa80b27 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,4 +1,3 @@ -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; 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 index 0e7f459fecd..dbe2eb34fd6 100644 --- 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 @@ -1,5 +1,6 @@ -use crate::abis::constant_rollup_data::ConstantRollupData; -use types::abis::combined_constant_data::CombinedConstantData; +use types::abis::{ + combined_constant_data::CombinedConstantData, constant_rollup_data::ConstantRollupData, +}; pub(crate) fn validate_combined_constant_data( constants: CombinedConstantData, 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 9e3e70f9433..128c7bed5bd 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 @@ -3,3 +3,10 @@ pub(crate) mod constants; pub(crate) mod fees; pub(crate) mod nullifier_tree; pub(crate) mod public_data_tree; + +mod private_tube_data_validator; +mod public_tube_data_validator; +pub(crate) mod validate_tube_data; + +pub(crate) use private_tube_data_validator::PrivateTubeDataValidator; +pub(crate) use public_tube_data_validator::PublicTubeDataValidator; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr new file mode 100644 index 00000000000..8027741142f --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr @@ -0,0 +1,28 @@ +use super::validate_tube_data::validate_max_fees_per_gas; +use dep::types::abis::{constant_rollup_data::ConstantRollupData, tube::PrivateTubeData}; + +pub struct PrivateTubeDataValidator { + pub data: PrivateTubeData, +} + +// TODO: Move relevant verifications here. +impl PrivateTubeDataValidator { + pub fn new(data: PrivateTubeData) -> Self { + PrivateTubeDataValidator { data } + } + + pub fn verify_proof(self, _allowed_previous_circuits: [u32; N]) { + if !dep::std::runtime::is_unconstrained() { + self.data.verify(); + // TODO(#7410) + // self.data.vk_data.validate_in_vk_tree(self.data.public_inputs.constants.vk_tree_root, allowed_previous_circuits); + } + } + + pub fn validate_with_rollup_data(self, constants: ConstantRollupData) { + validate_max_fees_per_gas( + self.data.public_inputs.constants.tx_context.gas_settings.max_fees_per_gas, + constants.global_variables.gas_fees, + ); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr new file mode 100644 index 00000000000..6edeb1ad88b --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr @@ -0,0 +1,28 @@ +use super::validate_tube_data::validate_max_fees_per_gas; +use dep::types::abis::{constant_rollup_data::ConstantRollupData, tube::PublicTubeData}; + +pub struct PublicTubeDataValidator { + pub data: PublicTubeData, +} + +// TODO: Move relevant verifications here. +impl PublicTubeDataValidator { + pub fn new(data: PublicTubeData) -> Self { + PublicTubeDataValidator { data } + } + + pub fn verify_proof(self) { + if !dep::std::runtime::is_unconstrained() { + self.data.verify(); + // TODO(#7410) + // self.tube_data.vk_data.validate_in_vk_tree(self.tube_data.public_inputs.constants.vk_tree_root, ALLOWED_PREVIOUS_CIRCUITS); + } + } + + pub fn validate_with_rollup_data(self, constants: ConstantRollupData) { + validate_max_fees_per_gas( + self.data.public_inputs.constants.tx_context.gas_settings.max_fees_per_gas, + constants.global_variables.gas_fees, + ); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr new file mode 100644 index 00000000000..3c815f2d235 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr @@ -0,0 +1,12 @@ +use dep::types::abis::gas_fees::GasFees; + +pub fn validate_max_fees_per_gas(max_fees_per_gas: GasFees, gas_fees: GasFees) { + assert( + !max_fees_per_gas.fee_per_da_gas.lt(gas_fees.fee_per_da_gas), + "da gas is higher than the maximum specified by the tx", + ); + assert( + !max_fees_per_gas.fee_per_l2_gas.lt(gas_fees.fee_per_l2_gas), + "l2 gas is higher than the maximum specified by the tx", + ); +} 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 03b442b4eaa..9b9bc52e9bd 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 @@ -2,6 +2,7 @@ pub(crate) mod components; pub(crate) mod state_diff_hints; mod private_base_rollup; mod public_base_rollup; +mod tests; 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 b5952b6b3d7..06bdc554757 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,13 +1,11 @@ use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, base::{ components::{ 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, + nullifier_tree::nullifier_tree_batch_insert, PrivateTubeDataValidator, + public_data_tree::public_data_tree_insert, }, state_diff_hints::PrivateBaseStateDiffHints, }, @@ -15,12 +13,13 @@ use crate::{ }; use dep::types::{ abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, constant_rollup_data::ConstantRollupData, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, sponge_blob::SpongeBlob, tube::PrivateTubeData, }, constants::{ ARCHIVE_HEIGHT, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT, + TUBE_VK_INDEX, }, data::{hash::compute_public_data_tree_value, public_data_hint::PublicDataHint}, hash::silo_l2_to_l1_message, @@ -32,7 +31,7 @@ use dep::types::{ traits::is_empty, }; -// global ALLOWED_PREVIOUS_CIRCUITS: [u32; 2] = [PRIVATE_KERNEL_EMPTY_INDEX, TUBE_VK_INDEX]; +global ALLOWED_PREVIOUS_CIRCUITS: [u32; 1] = [TUBE_VK_INDEX]; pub struct PrivateBaseRollupInputs { tube_data: PrivateTubeData, @@ -55,10 +54,14 @@ impl PrivateBaseRollupInputs { } pub fn execute(self) -> BaseOrMergeRollupPublicInputs { - if !dep::std::runtime::is_unconstrained() { - self.tube_data.verify(); - // TODO(#7410) - // self.tube_data.vk_data.validate_in_vk_tree(self.tube_data.public_inputs.constants.vk_tree_root, ALLOWED_PREVIOUS_CIRCUITS); + // TODO(#10311): There should be an empty base rollup. + // There's at least 1 nullifier for a tx. So gas_used won't be empty if the previous circuit is private_tail. + let is_empty_tube = self.tube_data.public_inputs.gas_used.is_empty(); + + let tube_data_validator = PrivateTubeDataValidator::new(self.tube_data); + tube_data_validator.verify_proof(ALLOWED_PREVIOUS_CIRCUITS); + if !is_empty_tube { + tube_data_validator.validate_with_rollup_data(self.constants); } let transaction_fee = self.compute_transaction_fee(); @@ -223,10 +226,7 @@ impl PrivateBaseRollupInputs { mod tests { use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, base::{ components::fees::compute_fee_payer_fee_juice_balance_leaf_slot, private_base_rollup::PrivateBaseRollupInputs, @@ -239,7 +239,8 @@ mod tests { use dep::types::{ abis::{ accumulated_data::CombinedAccumulatedData, - append_only_tree_snapshot::AppendOnlyTreeSnapshot, gas::Gas, gas_fees::GasFees, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + constant_rollup_data::ConstantRollupData, gas::Gas, gas_fees::GasFees, kernel_circuit_public_inputs::KernelCircuitPublicInputs, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, sponge_blob::SpongeBlob, @@ -887,6 +888,8 @@ mod tests { builder.tube_data.set_gas_used(100, 200); builder.constants.global_variables.gas_fees.fee_per_da_gas = 1; builder.constants.global_variables.gas_fees.fee_per_l2_gas = 1; + builder.tube_data.tx_context.gas_settings.max_fees_per_gas.fee_per_da_gas = 1; + builder.tube_data.tx_context.gas_settings.max_fees_per_gas.fee_per_l2_gas = 1; let tx_fee = builder.compute_transaction_fee(); // builder.transaction_fee = tx_fee; builder.tube_data.append_note_hashes_with_logs(NUM_NOTES); @@ -1118,6 +1121,7 @@ mod tests { // Set gas builder.tube_data.gas_used = gas_used; + builder.tube_data.tx_context.gas_settings.max_fees_per_gas = gas_fees; builder.constants.global_variables.gas_fees = gas_fees; // Set fee payer @@ -1167,6 +1171,7 @@ mod tests { // Set gas builder.tube_data.gas_used = gas_used; + builder.tube_data.tx_context.gas_settings.max_fees_per_gas = gas_fees; builder.constants.global_variables.gas_fees = gas_fees; // Set fee payer @@ -1197,6 +1202,7 @@ mod tests { // Set gas builder.tube_data.gas_used = gas_used; + builder.tube_data.tx_context.gas_settings.max_fees_per_gas = gas_fees; builder.constants.global_variables.gas_fees = gas_fees; // Set fee payer 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 3b680e66e7c..827d87973b0 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,12 +1,10 @@ use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, base::{ components::{ archive::perform_archive_membership_check, constants::validate_combined_constant_data, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, + PublicTubeDataValidator, }, state_diff_hints::PublicBaseStateDiffHints, }, @@ -18,6 +16,7 @@ use dep::types::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, avm_circuit_public_inputs::AvmProofData, combined_constant_data::CombinedConstantData, + constant_rollup_data::ConstantRollupData, log_hash::ScopedLogHash, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, @@ -92,11 +91,9 @@ impl PublicBaseRollupInputs { } pub fn execute(self) -> BaseOrMergeRollupPublicInputs { - if !dep::std::runtime::is_unconstrained() { - self.tube_data.verify(); - // TODO(#7410) - // self.tube_data.vk_data.validate_in_vk_tree([TUBE_VK_INDEX]); - } + let tube_data_validator = PublicTubeDataValidator::new(self.tube_data); + tube_data_validator.verify_proof(); + tube_data_validator.validate_with_rollup_data(self.constants); // Warning: Fake verification! TODO(#8470) // if !dep::std::runtime::is_unconstrained() { @@ -263,10 +260,7 @@ impl PublicBaseRollupInputs { mod tests { use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, base::{ public_base_rollup::PublicBaseRollupInputs, state_diff_hints::PublicBaseStateDiffHints, }, @@ -278,6 +272,7 @@ mod tests { abis::{ accumulated_data::CombinedAccumulatedData, append_only_tree_snapshot::AppendOnlyTreeSnapshot, + constant_rollup_data::ConstantRollupData, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, sponge_blob::SpongeBlob, }, @@ -298,7 +293,11 @@ mod tests { merkle_tree::MembershipWitness, messaging::l2_to_l1_message::ScopedL2ToL1Message, partial_state_reference::PartialStateReference, - tests::{fixture_builder::FixtureBuilder, fixtures, merkle_tree_utils::NonEmptyMerkleTree}, + tests::{ + fixture_builder::FixtureBuilder, + fixtures::{self, merkle_tree::generate_full_sha_tree}, + merkle_tree_utils::NonEmptyMerkleTree, + }, traits::{Empty, is_empty}, utils::{ arrays::{array_concat, get_sorted_tuple::get_sorted_tuple}, @@ -1070,9 +1069,7 @@ mod tests { ); // 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(), - ); + let expected_tree = generate_full_sha_tree(siloed_l2_to_l1_msgs.storage()); assert_eq(out_hash, expected_tree.get_root()); } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr new file mode 100644 index 00000000000..7e88fe33e7d --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr @@ -0,0 +1 @@ +mod private_tube_data_validator_builder; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr new file mode 100644 index 00000000000..cefbf64d37d --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr @@ -0,0 +1,24 @@ +mod validate_with_rollup_data; + +use crate::base::components::PrivateTubeDataValidator; +use dep::types::tests::fixture_builder::FixtureBuilder; + +pub struct PrivateTubeDataValidatorBuilder { + pub tube_data: FixtureBuilder, + pub rollup_data: FixtureBuilder, +} + +impl PrivateTubeDataValidatorBuilder { + pub fn new() -> Self { + PrivateTubeDataValidatorBuilder { + tube_data: FixtureBuilder::new(), + rollup_data: FixtureBuilder::new(), + } + } + + pub fn validate_with_rollup_data(self) { + let tube_data = self.tube_data.to_private_tube_data(); + let rollup_data = self.rollup_data.to_constant_rollup_data(); + PrivateTubeDataValidator::new(tube_data).validate_with_rollup_data(rollup_data); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr new file mode 100644 index 00000000000..64756b1efb9 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr @@ -0,0 +1,27 @@ +use super::PrivateTubeDataValidatorBuilder; + +#[test] +fn validate_with_rollup_data_success() { + let builder = PrivateTubeDataValidatorBuilder::new(); + builder.validate_with_rollup_data(); +} + +#[test(should_fail_with = "da gas is higher than the maximum specified by the tx")] +fn validate_with_rollup_data_not_enough_fee_per_da_gas_fails() { + let mut builder = PrivateTubeDataValidatorBuilder::new(); + + builder.tube_data.tx_context.gas_settings.max_fees_per_gas.fee_per_da_gas = 3; + builder.rollup_data.global_variables.gas_fees.fee_per_da_gas = 4; + + builder.validate_with_rollup_data(); +} + +#[test(should_fail_with = "l2 gas is higher than the maximum specified by the tx")] +fn validate_with_rollup_data_not_enough_fee_per_l2_gas_fails() { + let mut builder = PrivateTubeDataValidatorBuilder::new(); + + builder.tube_data.tx_context.gas_settings.max_fees_per_gas.fee_per_l2_gas = 3; + builder.rollup_data.global_variables.gas_fees.fee_per_l2_gas = 4; + + builder.validate_with_rollup_data(); +} 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_circuit_public_inputs.nr index 0352937e504..88fe770eb0d 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_circuit_public_inputs.nr @@ -214,7 +214,7 @@ impl AvmProofData { ); let mut result: [Field; 4] = [input_hash, 0, 0, 0]; - for i in 0..DUMMY_AVM_VERIFIER_NUM_ITERATIONS { + for _i in 0..DUMMY_AVM_VERIFIER_NUM_ITERATIONS { result = poseidon2_permutation(result, 4); } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/constant_rollup_data.nr similarity index 99% rename from noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/abis/constant_rollup_data.nr index 9eab56a2092..0a8d6919164 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/constant_rollup_data.nr @@ -1,4 +1,4 @@ -use dep::types::{ +use crate::{ abis::{append_only_tree_snapshot::AppendOnlyTreeSnapshot, global_variables::GlobalVariables}, constants::CONSTANT_ROLLUP_DATA_LENGTH, traits::{Deserialize, Empty, Serialize}, 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 f1bce9e245c..ff429eefad6 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 @@ -13,6 +13,7 @@ pub mod nullifier_leaf_preimage; pub mod tx_constant_data; pub mod combined_constant_data; +pub mod constant_rollup_data; pub mod side_effect; pub mod read_request; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel/private_call_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel/private_call_data.nr index 0d887f71f42..37c1b1dca94 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel/private_call_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel/private_call_data.nr @@ -25,7 +25,7 @@ pub struct PrivateCallData { } impl PrivateCallData { - fn verify(self, is_first_app: bool) { + pub fn verify(self, is_first_app: bool) { let proof_type = if is_first_app { PROOF_TYPE_OINK } else { 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 dc3940d0557..404bd4cbc7e 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,9 +4,11 @@ use crate::{ avm_accumulated_data::AvmAccumulatedData, CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PrivateToPublicAccumulatedData, }, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, avm_circuit_public_inputs::AvmProofData, call_context::CallContext, combined_constant_data::CombinedConstantData, + constant_rollup_data::ConstantRollupData, function_data::FunctionData, gas::Gas, gas_settings::GasSettings, @@ -157,6 +159,9 @@ pub struct FixtureBuilder { pub protocol_contract_tree_root: Field, pub protocol_contract_sibling_path: [Field; PROTOCOL_CONTRACT_TREE_HEIGHT], + // Tree snapshots. + pub archive_tree: AppendOnlyTreeSnapshot, + // Counters. pub min_revertible_side_effect_counter: u32, pub counter_start: u32, @@ -216,6 +221,10 @@ impl FixtureBuilder { *self } + pub fn vk_tree_root() -> Field { + fixtures::vk_tree::get_vk_merkle_tree().get_root() + } + pub fn set_protocol_contract_root(&mut self) { let tree = fixtures::protocol_contract_tree::get_protocol_contract_tree(); self.protocol_contract_tree_root = tree.get_root(); @@ -289,6 +298,15 @@ impl FixtureBuilder { } } + pub fn to_constant_rollup_data(self) -> ConstantRollupData { + ConstantRollupData { + last_archive: self.archive_tree, + vk_tree_root: self.vk_tree_root, + protocol_contract_tree_root: self.protocol_contract_tree_root, + global_variables: self.global_variables, + } + } + pub fn build_tx_request(self) -> TxRequest { TxRequest { origin: self.contract_address, @@ -525,6 +543,38 @@ impl FixtureBuilder { } } + pub fn to_private_tube_data(self) -> PrivateTubeData { + let mut result: PrivateTubeData = std::mem::zeroed(); + result.public_inputs = self.to_kernel_circuit_public_inputs(); + result + } + + pub 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 + } + + pub 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(), + } + } + + pub 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 + } + pub fn add_new_note_hash(&mut self, value: Field) { self.note_hashes.push(NoteHash { value, counter: self.next_counter() }.scope( self.contract_address, @@ -1037,42 +1087,6 @@ impl FixtureBuilder { self.counter += 1; counter } - - fn vk_tree_root() -> Field { - fixtures::vk_tree::get_vk_merkle_tree().get_root() - } - - pub fn to_private_tube_data(self) -> PrivateTubeData { - let mut result: PrivateTubeData = std::mem::zeroed(); - result.public_inputs = self.to_kernel_circuit_public_inputs(); - result - } - - pub 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 - } - - pub 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(), - } - } - - pub 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 { @@ -1123,6 +1137,7 @@ impl Empty for FixtureBuilder { vk_tree_root: FixtureBuilder::vk_tree_root(), protocol_contract_tree_root: 0, protocol_contract_sibling_path: [0; PROTOCOL_CONTRACT_TREE_HEIGHT], + archive_tree: AppendOnlyTreeSnapshot::zero(), revert_code: 0, min_revertible_side_effect_counter: 0, counter_start: 0, diff --git a/yarn-project/circuit-types/src/tx/validator/empty_validator.ts b/yarn-project/circuit-types/src/tx/validator/empty_validator.ts index 7ad6f80dcf1..2ea10e7a55a 100644 --- a/yarn-project/circuit-types/src/tx/validator/empty_validator.ts +++ b/yarn-project/circuit-types/src/tx/validator/empty_validator.ts @@ -1,8 +1,8 @@ import { type AnyTx, type TxValidator } from './tx_validator.js'; export class EmptyTxValidator implements TxValidator { - public validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[]]> { - return Promise.resolve([txs, []]); + public validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[], skippedTxs: T[]]> { + return Promise.resolve([txs, [], []]); } public validateTx(_tx: T): Promise { diff --git a/yarn-project/circuit-types/src/tx/validator/tx_validator.ts b/yarn-project/circuit-types/src/tx/validator/tx_validator.ts index 6669b56055a..040d764cf3d 100644 --- a/yarn-project/circuit-types/src/tx/validator/tx_validator.ts +++ b/yarn-project/circuit-types/src/tx/validator/tx_validator.ts @@ -5,5 +5,5 @@ export type AnyTx = Tx | ProcessedTx; export interface TxValidator { validateTx(tx: T): Promise; - validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[]]>; + validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[], skippedTxs?: T[]]>; } diff --git a/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.test.ts b/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.test.ts index c74a0fc5e16..bd315664445 100644 --- a/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.test.ts +++ b/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.test.ts @@ -6,24 +6,53 @@ describe('AggregateTxValidator', () => { it('allows txs that pass all validation', async () => { const txs = [mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]; const agg = new AggregateTxValidator( - new TxDenyList(txs[0].getTxHash(), txs[1].getTxHash()), - new TxDenyList(txs[2].getTxHash(), txs[3].getTxHash()), + new TxDenyList([txs[0].getTxHash(), txs[1].getTxHash()], []), + new TxDenyList([txs[2].getTxHash(), txs[4].getTxHash()], []), ); - await expect(agg.validateTxs(txs)).resolves.toEqual([[txs[4]], [txs[0], txs[1], txs[2], txs[3]]]); + const validTxs = [txs[3]]; + const invalidTxs = [txs[0], txs[1], txs[2], txs[4]]; + const skippedTxs: AnyTx[] = []; + await expect(agg.validateTxs(txs)).resolves.toEqual([validTxs, invalidTxs, skippedTxs]); + }); + + it('aggregate skipped txs ', async () => { + const txs = [mockTx(0), mockTx(1), mockTx(2), mockTx(3), mockTx(4)]; + const agg = new AggregateTxValidator( + new TxDenyList([txs[0].getTxHash()], []), + new TxDenyList([], [txs[1].getTxHash(), txs[2].getTxHash()]), + new TxDenyList([], [txs[4].getTxHash()]), + ); + + const validTxs = [txs[3]]; + const invalidTxs = [txs[0]]; + const skippedTxs = [txs[1], txs[2], txs[4]]; + await expect(agg.validateTxs(txs)).resolves.toEqual([validTxs, invalidTxs, skippedTxs]); }); class TxDenyList implements TxValidator { denyList: Set; - constructor(...txHashes: TxHash[]) { - this.denyList = new Set(txHashes.map(hash => hash.toString())); + skippedList: Set; + constructor(deniedTxHashes: TxHash[], skippedTxHashes: TxHash[]) { + this.denyList = new Set(deniedTxHashes.map(hash => hash.toString())); + this.skippedList = new Set(skippedTxHashes.map(hash => hash.toString())); } - validateTxs(txs: AnyTx[]): Promise<[AnyTx[], AnyTx[]]> { - return Promise.resolve([ - txs.filter(tx => !this.denyList.has(Tx.getHash(tx).toString())), - txs.filter(tx => this.denyList.has(Tx.getHash(tx).toString())), - ]); + validateTxs(txs: AnyTx[]): Promise<[AnyTx[], AnyTx[], AnyTx[] | undefined]> { + const validTxs: AnyTx[] = []; + const invalidTxs: AnyTx[] = []; + const skippedTxs: AnyTx[] = []; + txs.forEach(tx => { + const txHash = Tx.getHash(tx).toString(); + if (this.skippedList.has(txHash)) { + skippedTxs.push(tx); + } else if (this.denyList.has(txHash)) { + invalidTxs.push(tx); + } else { + validTxs.push(tx); + } + }); + return Promise.resolve([validTxs, invalidTxs, skippedTxs.length ? skippedTxs : undefined]); } validateTx(tx: AnyTx): Promise { diff --git a/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.ts b/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.ts index 99dfb6c282d..21bf24ddb8d 100644 --- a/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.ts +++ b/yarn-project/p2p/src/tx_validator/aggregate_tx_validator.ts @@ -10,16 +10,18 @@ export class AggregateTxValidator implements TxValid this.#validators = validators; } - async validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[]]> { + async validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[], skippedTxs: T[]]> { const invalidTxs: T[] = []; + const skippedTxs: T[] = []; let txPool = txs; for (const validator of this.#validators) { - const [valid, invalid] = await validator.validateTxs(txPool); + const [valid, invalid, skipped] = await validator.validateTxs(txPool); invalidTxs.push(...invalid); + skippedTxs.push(...(skipped ?? [])); txPool = valid; } - return [txPool, invalidTxs]; + return [txPool, invalidTxs, skippedTxs]; } async validateTx(tx: T): Promise { diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 5b7943d7025..72498b7e86c 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -26,6 +26,7 @@ import { EthAddress, Fr, GasFees, + type GasSettings, GlobalVariables, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, } from '@aztec/circuits.js'; @@ -390,6 +391,47 @@ describe('sequencer', () => { expect(publisher.proposeL2Block).toHaveBeenCalledWith(block, getSignatures(), validTxHashes, undefined); }); + it('builds a block out of several txs skipping the ones not providing enough fee per gas', async () => { + const gasFees = new GasFees(10, 20); + mockedGlobalVariables.gasFees = gasFees; + + const txs = Array(5) + .fill(0) + .map((_, i) => mockTxForRollup(0x10000 * i)); + + const skippedTxIndexes = [1, 2]; + const validTxHashes: TxHash[] = []; + txs.forEach((tx, i) => { + tx.data.constants.txContext.chainId = chainId; + const maxFeesPerGas: Writeable = gasFees.clone(); + const feeToAdjust = i % 2 ? 'feePerDaGas' : 'feePerL2Gas'; + if (skippedTxIndexes.includes(i)) { + // maxFeesPerGas is less than gasFees. + maxFeesPerGas[feeToAdjust] = maxFeesPerGas[feeToAdjust].sub(new Fr(i + 1)); + } else { + // maxFeesPerGas is greater than or equal to gasFees. + maxFeesPerGas[feeToAdjust] = maxFeesPerGas[feeToAdjust].add(new Fr(i)); + validTxHashes.push(tx.getTxHash()); + } + (tx.data.constants.txContext.gasSettings as Writeable).maxFeesPerGas = maxFeesPerGas; + }); + + p2p.getPendingTxs.mockResolvedValueOnce(txs); + blockBuilder.setBlockCompleted.mockResolvedValue(block); + publisher.proposeL2Block.mockResolvedValueOnce(true); + globalVariableBuilder.buildGlobalVariables.mockResolvedValueOnce(mockedGlobalVariables); + + await sequencer.doRealWork(); + + expect(blockBuilder.startNewBlock).toHaveBeenCalledWith( + mockedGlobalVariables, + Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)), + ); + expect(publisher.proposeL2Block).toHaveBeenCalledWith(block, getSignatures(), validTxHashes, undefined); + // The txs are not included. But they are not dropped from the pool either. + expect(p2p.deleteTxs).not.toHaveBeenCalled(); + }); + it('builds a block once it reaches the minimum number of transactions', async () => { const txs = times(8, i => { const tx = mockTxForRollup(i * 0x10000); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts index 95210a1b69a..eec83ad034f 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts @@ -1,6 +1,7 @@ import { type Tx, mockTx } from '@aztec/circuit-types'; import { AztecAddress, Fr, FunctionSelector, GasFees, GasSettings, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js'; import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { type Writeable } from '@aztec/foundation/types'; import { FeeJuiceContract } from '@aztec/noir-contracts.js'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; @@ -10,28 +11,28 @@ import { GasTxValidator, type PublicStateSource } from './gas_validator.js'; import { patchNonRevertibleFn, patchRevertibleFn } from './test_utils.js'; describe('GasTxValidator', () => { - let validator: GasTxValidator; + // Vars for validator. let publicStateSource: MockProxy; let feeJuiceAddress: AztecAddress; - - beforeEach(() => { - feeJuiceAddress = ProtocolContractAddress.FeeJuice; - publicStateSource = mock({ - storageRead: mockFn().mockImplementation((_address: AztecAddress, _slot: Fr) => Fr.ZERO), - }); - - validator = new GasTxValidator(publicStateSource, feeJuiceAddress, false); - }); - + let enforceFees: boolean; + let gasFees: Writeable; + // Vars for tx. let tx: Tx; let payer: AztecAddress; let expectedBalanceSlot: Fr; let feeLimit: bigint; beforeEach(() => { + publicStateSource = mock({ + storageRead: mockFn().mockImplementation((_address: AztecAddress, _slot: Fr) => Fr.ZERO), + }); + feeJuiceAddress = ProtocolContractAddress.FeeJuice; + enforceFees = false; + gasFees = new GasFees(11, 22); + tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); tx.data.feePayer = AztecAddress.random(); - tx.data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }); + tx.data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: gasFees.clone() }); payer = tx.data.feePayer; expectedBalanceSlot = poseidon2Hash([FeeJuiceContract.storage.balances.slot, payer]); feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit().toBigInt(); @@ -43,21 +44,29 @@ describe('GasTxValidator', () => { ); }; - const expectValidateSuccess = async (tx: Tx) => { - const result = await validator.validateTxs([tx]); - expect(result[0].length).toEqual(1); - expect(result).toEqual([[tx], []]); + const validateTx = async (tx: Tx) => { + const validator = new GasTxValidator(publicStateSource, feeJuiceAddress, enforceFees, gasFees); + return await validator.validateTxs([tx]); }; - const expectValidateFail = async (tx: Tx) => { - const result = await validator.validateTxs([tx]); - expect(result[1].length).toEqual(1); - expect(result).toEqual([[], [tx]]); + const expectValid = async (tx: Tx) => { + const result = await validateTx(tx); + expect(result).toEqual([[tx], [], []]); + }; + + const expectInvalid = async (tx: Tx) => { + const result = await validateTx(tx); + expect(result).toEqual([[], [tx], []]); + }; + + const expectSkipped = async (tx: Tx) => { + const result = await validateTx(tx); + expect(result).toEqual([[], [], [tx]]); }; it('allows fee paying txs if fee payer has enough balance', async () => { mockBalance(feeLimit); - await expectValidateSuccess(tx); + await expectValid(tx); }); it('allows fee paying txs if fee payer claims enough balance during setup', async () => { @@ -69,16 +78,16 @@ describe('GasTxValidator', () => { args: [selector.toField(), payer.toField(), new Fr(1n)], msgSender: ProtocolContractAddress.FeeJuice, }); - await expectValidateSuccess(tx); + await expectValid(tx); }); it('rejects txs if fee payer has not enough balance', async () => { mockBalance(feeLimit - 1n); - await expectValidateFail(tx); + await expectInvalid(tx); }); it('rejects txs if fee payer has zero balance', async () => { - await expectValidateFail(tx); + await expectInvalid(tx); }); it('rejects txs if fee payer claims balance outside setup', async () => { @@ -87,17 +96,27 @@ describe('GasTxValidator', () => { selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'), args: [payer.toField(), new Fr(1n)], }); - await expectValidateFail(tx); + await expectInvalid(tx); }); it('allows txs with no fee payer if fees are not enforced', async () => { tx.data.feePayer = AztecAddress.ZERO; - await expectValidateSuccess(tx); + await expectValid(tx); }); it('rejects txs with no fee payer if fees are enforced', async () => { - validator.enforceFees = true; + enforceFees = true; tx.data.feePayer = AztecAddress.ZERO; - await expectValidateFail(tx); + await expectInvalid(tx); + }); + + it('skips txs with not enough fee per da gas', async () => { + gasFees.feePerDaGas = gasFees.feePerDaGas.add(new Fr(1)); + await expectSkipped(tx); + }); + + it('skips txs with not enough fee per l2 gas', async () => { + gasFees.feePerL2Gas = gasFees.feePerL2Gas.add(new Fr(1)); + await expectSkipped(tx); }); }); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts index 19ba9c2a79e..8b4167543c9 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts @@ -1,5 +1,5 @@ import { type Tx, TxExecutionPhase, type TxValidator } from '@aztec/circuit-types'; -import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js'; +import { type AztecAddress, type Fr, FunctionSelector, type GasFees } from '@aztec/circuits.js'; import { createLogger } from '@aztec/foundation/log'; import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator'; @@ -12,36 +12,62 @@ export class GasTxValidator implements TxValidator { #log = createLogger('sequencer:tx_validator:tx_gas'); #publicDataSource: PublicStateSource; #feeJuiceAddress: AztecAddress; + #enforceFees: boolean; + #gasFees: GasFees; - constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, public enforceFees: boolean) { + constructor( + publicDataSource: PublicStateSource, + feeJuiceAddress: AztecAddress, + enforceFees: boolean, + gasFees: GasFees, + ) { this.#publicDataSource = publicDataSource; this.#feeJuiceAddress = feeJuiceAddress; + this.#enforceFees = enforceFees; + this.#gasFees = gasFees; } - async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> { + async validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[], skippedTxs: Tx[]]> { const validTxs: Tx[] = []; const invalidTxs: Tx[] = []; + const skippedTxs: Tx[] = []; for (const tx of txs) { - if (await this.#validateTxFee(tx)) { + if (this.#shouldSkip(tx)) { + skippedTxs.push(tx); + } else if (await this.#validateTxFee(tx)) { validTxs.push(tx); } else { invalidTxs.push(tx); } } - return [validTxs, invalidTxs]; + return [validTxs, invalidTxs, skippedTxs]; } validateTx(tx: Tx): Promise { return this.#validateTxFee(tx); } + #shouldSkip(tx: Tx): boolean { + const gasSettings = tx.data.constants.txContext.gasSettings; + + // Skip the tx if its max fees are not enough for the current block's gas fees. + const maxFeesPerGas = gasSettings.maxFeesPerGas; + const notEnoughMaxFees = + maxFeesPerGas.feePerDaGas.lt(this.#gasFees.feePerDaGas) || + maxFeesPerGas.feePerL2Gas.lt(this.#gasFees.feePerL2Gas); + if (notEnoughMaxFees) { + this.#log.warn(`Skipping transaction ${tx.getTxHash()} due to insufficient fee per gas`); + } + return notEnoughMaxFees; + } + async #validateTxFee(tx: Tx): Promise { const feePayer = tx.data.feePayer; // TODO(@spalladino) Eventually remove the is_zero condition as we should always charge fees to every tx if (feePayer.isZero()) { - if (this.enforceFees) { + if (this.#enforceFees) { this.#log.warn(`Rejecting transaction ${tx.getTxHash()} due to missing fee payer`); } else { return true; diff --git a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts index dd36e4ebc3d..a3647b2d710 100644 --- a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts +++ b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts @@ -46,7 +46,12 @@ export class TxValidatorFactory { new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber), new DoubleSpendTxValidator(this.nullifierSource), new PhasesTxValidator(this.contractDataSource, setupAllowList), - new GasTxValidator(this.publicStateSource, ProtocolContractAddress.FeeJuice, this.enforceFees), + new GasTxValidator( + this.publicStateSource, + ProtocolContractAddress.FeeJuice, + this.enforceFees, + globalVariables.gasFees, + ), ); } From 381b0b84d87dd31f8ab5a3e62928f9992837d4c0 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:56:30 -0500 Subject: [PATCH 02/10] feat: Sync from noir (#10760) Automated pull of development from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE chore(ci): add bloblib to external checks (https://github.com/noir-lang/noir/pull/6818) chore(docs): workaround (https://github.com/noir-lang/noir/pull/6819) chore: manage call stacks using a tree (https://github.com/noir-lang/noir/pull/6791) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .noir-sync-commit | 2 +- .../.github/workflows/test-js-packages.yml | 1 + .../noirc_evaluator/src/acir/acir_variable.rs | 2 +- .../src/acir/generated_acir.rs | 2 +- .../compiler/noirc_evaluator/src/acir/mod.rs | 14 +- .../src/brillig/brillig_gen.rs | 2 +- .../src/brillig/brillig_gen/brillig_block.rs | 4 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 2 +- .../src/brillig/brillig_ir/artifact.rs | 2 +- .../compiler/noirc_evaluator/src/errors.rs | 2 +- .../check_for_underconstrained_values.rs | 4 +- .../src/ssa/function_builder/mod.rs | 28 ++-- .../noirc_evaluator/src/ssa/ir/basic_block.rs | 4 +- .../noirc_evaluator/src/ssa/ir/call_stack.rs | 128 ++++++++++++++++++ .../noirc_evaluator/src/ssa/ir/cfg.rs | 16 +-- .../noirc_evaluator/src/ssa/ir/dfg.rs | 44 ++++-- .../noirc_evaluator/src/ssa/ir/dom.rs | 9 +- .../src/ssa/ir/function_inserter.rs | 9 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 31 +++-- .../src/ssa/ir/instruction/call.rs | 60 +++----- .../src/ssa/ir/instruction/call/blackbox.rs | 28 ++-- .../noirc_evaluator/src/ssa/ir/mod.rs | 1 + .../src/ssa/opt/assert_constant.rs | 6 +- .../src/ssa/opt/constant_folding.rs | 4 +- .../noirc_evaluator/src/ssa/opt/die.rs | 18 +-- .../src/ssa/opt/flatten_cfg.rs | 89 +++++------- .../src/ssa/opt/flatten_cfg/value_merger.rs | 63 +++------ .../noirc_evaluator/src/ssa/opt/inlining.rs | 74 +++++++--- .../src/ssa/opt/loop_invariant.rs | 6 +- .../src/ssa/opt/normalize_value_ids.rs | 11 +- .../src/ssa/opt/remove_bit_shifts.rs | 12 +- .../src/ssa/opt/remove_if_else.rs | 2 +- .../src/ssa/opt/simplify_cfg.rs | 4 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 19 ++- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 10 +- .../docs/docs/getting_started/quick_start.md | 6 +- .../getting_started/quick_start.md | 4 +- .../getting_started/quick_start.md | 6 +- .../getting_started/quick_start.md | 6 +- .../getting_started/quick_start.md | 6 +- .../getting_started/quick_start.md | 6 +- 41 files changed, 459 insertions(+), 288 deletions(-) create mode 100644 noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/call_stack.rs diff --git a/.noir-sync-commit b/.noir-sync-commit index c5e4ffccf9e..a6780a1e9ef 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -e88deaf4890a3c6d6af8b0760e952d10573c004d \ No newline at end of file +f337992de96ef656681ebfc96a30c2c9c9b82a70 \ No newline at end of file diff --git a/noir/noir-repo/.github/workflows/test-js-packages.yml b/noir/noir-repo/.github/workflows/test-js-packages.yml index dde0deed0cf..d227b4eb00b 100644 --- a/noir/noir-repo/.github/workflows/test-js-packages.yml +++ b/noir/noir-repo/.github/workflows/test-js-packages.yml @@ -553,6 +553,7 @@ jobs: include: - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/aztec-nr } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-contracts } + - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/blob } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/parity-lib } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/private-kernel-lib } - project: { repo: AztecProtocol/aztec-packages, path: noir-projects/noir-protocol-circuits/crates/reset-kernel-lib } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs index 9f2c649ee3e..78188ea2b65 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/acir_variable.rs @@ -23,7 +23,7 @@ use std::{borrow::Cow, hash::Hash}; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; use crate::errors::{InternalBug, InternalError, RuntimeError, SsaReport}; use crate::ssa::ir::{ - dfg::CallStack, instruction::Endian, types::NumericType, types::Type as SsaType, + call_stack::CallStack, instruction::Endian, types::NumericType, types::Type as SsaType, }; use super::big_int::BigIntContext; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs index 3b29c0319ab..b6a5a817ea7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/generated_acir.rs @@ -16,7 +16,7 @@ use super::brillig_directive; use crate::{ brillig::brillig_ir::artifact::GeneratedBrillig, errors::{InternalError, RuntimeError, SsaReport}, - ssa::ir::dfg::CallStack, + ssa::ir::call_stack::CallStack, ErrorType, }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs index 769d0d80cc4..e7b011b6d7b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/acir/mod.rs @@ -35,7 +35,8 @@ use crate::ssa::ir::instruction::Hint; use crate::ssa::{ function_builder::data_bus::DataBus, ir::{ - dfg::{CallStack, DataFlowGraph}, + call_stack::CallStack, + dfg::DataFlowGraph, function::{Function, FunctionId, RuntimeType}, instruction::{ Binary, BinaryOp, ConstrainError, Instruction, InstructionId, Intrinsic, @@ -675,7 +676,7 @@ impl<'a> Context<'a> { brillig: &Brillig, ) -> Result, RuntimeError> { let instruction = &dfg[instruction_id]; - self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); + self.acir_context.set_call_stack(dfg.get_instruction_call_stack(instruction_id)); let mut warnings = Vec::new(); match instruction { Instruction::Binary(binary) => { @@ -1822,7 +1823,7 @@ impl<'a> Context<'a> { ) -> Result<(Vec, Vec), RuntimeError> { let (return_values, call_stack) = match terminator { TerminatorInstruction::Return { return_values, call_stack } => { - (return_values, call_stack.clone()) + (return_values, *call_stack) } // TODO(https://github.com/noir-lang/noir/issues/4616): Enable recursion on foldable/non-inlined ACIR functions _ => unreachable!("ICE: Program must have a singular return"), @@ -1841,6 +1842,7 @@ impl<'a> Context<'a> { } } + let call_stack = dfg.call_stack_data.get_call_stack(call_stack); let warnings = if has_constant_return { vec![SsaReport::Warning(InternalWarning::ReturnConstant { call_stack })] } else { @@ -2903,7 +2905,7 @@ mod test { ssa::{ function_builder::FunctionBuilder, ir::{ - dfg::CallStack, + call_stack::CallStack, function::FunctionId, instruction::BinaryOp, map::Id, @@ -2932,7 +2934,9 @@ mod test { // Set a call stack for testing whether `brillig_locations` in the `GeneratedAcir` was accurately set. let mut stack = CallStack::unit(Location::dummy()); stack.push_back(Location::dummy()); - builder.set_call_stack(stack); + let call_stack = + builder.current_function.dfg.call_stack_data.get_or_insert_locations(stack); + builder.set_call_stack(call_stack); let foo_v0 = builder.add_parameter(Type::field()); let foo_v1 = builder.add_parameter(Type::field()); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ca4e783aa93..5a81c79ae0d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -18,7 +18,7 @@ use super::{ }; use crate::{ errors::InternalError, - ssa::ir::{dfg::CallStack, function::Function}, + ssa::ir::{call_stack::CallStack, function::Function}, }; /// Converting an SSA function into Brillig bytecode. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index d2bf7e5bdca..0b02cd29f0f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -7,7 +7,7 @@ use crate::brillig::brillig_ir::registers::Stack; use crate::brillig::brillig_ir::{ BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; -use crate::ssa::ir::dfg::CallStack; +use crate::ssa::ir::call_stack::CallStack; use crate::ssa::ir::instruction::{ConstrainError, Hint}; use crate::ssa::ir::{ basic_block::BasicBlockId, @@ -201,7 +201,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA instruction into a sequence of Brillig opcodes. fn convert_ssa_instruction(&mut self, instruction_id: InstructionId, dfg: &DataFlowGraph) { let instruction = &dfg[instruction_id]; - self.brillig_context.set_call_stack(dfg.get_call_stack(instruction_id)); + self.brillig_context.set_call_stack(dfg.get_instruction_call_stack(instruction_id)); self.initialize_constants( &self.function_context.constant_allocation.allocated_at_location( diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 8d5f14cee94..3c100d229a6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -30,7 +30,7 @@ pub(crate) use instructions::BrilligBinaryOp; use registers::{RegisterAllocator, ScratchSpace}; use self::{artifact::BrilligArtifact, debug_show::DebugToString, registers::Stack}; -use crate::ssa::ir::dfg::CallStack; +use crate::ssa::ir::call_stack::CallStack; use acvm::{ acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, AcirField, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 0e5b72c0b85..be4a6b84bc1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -2,7 +2,7 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use acvm::acir::circuit::ErrorSelector; use std::collections::{BTreeMap, HashMap}; -use crate::ssa::ir::{basic_block::BasicBlockId, dfg::CallStack, function::FunctionId}; +use crate::ssa::ir::{basic_block::BasicBlockId, call_stack::CallStack, function::FunctionId}; use crate::ErrorType; use super::procedures::ProcedureId; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs index bb224617994..ee662c50b75 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs @@ -12,7 +12,7 @@ use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::ssa::ir::{dfg::CallStack, types::NumericType}; +use crate::ssa::ir::{call_stack::CallStack, types::NumericType}; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq, Clone, Error)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index 5a857ae24ab..48be8eb7ad8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -383,7 +383,7 @@ impl DependencyContext { .keys() .map(|brillig_call| { SsaReport::Bug(InternalBug::UncheckedBrilligCall { - call_stack: function.dfg.get_call_stack(*brillig_call), + call_stack: function.dfg.get_instruction_call_stack(*brillig_call), }) }) .collect(); @@ -514,7 +514,7 @@ impl Context { // There is a value not in the set, which means that the inputs/outputs of this call have not been properly constrained if unused_inputs { warnings.push(SsaReport::Bug(InternalBug::IndependentSubgraph { - call_stack: function.dfg.get_call_stack( + call_stack: function.dfg.get_instruction_call_stack( self.brillig_return_to_instruction_id[&brillig_output_in_set], ), })); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index fe654912ad3..855034cedd2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -18,7 +18,8 @@ use crate::ssa::ir::{ use super::{ ir::{ basic_block::BasicBlock, - dfg::{CallStack, InsertInstructionResult}, + call_stack::{CallStack, CallStackId}, + dfg::InsertInstructionResult, function::RuntimeType, instruction::{ConstrainError, InstructionId, Intrinsic}, types::NumericType, @@ -34,10 +35,10 @@ use super::{ /// Contrary to the name, this struct has the capacity to build as many /// functions as needed, although it is limited to one function at a time. pub(crate) struct FunctionBuilder { - pub(super) current_function: Function, + pub(crate) current_function: Function, current_block: BasicBlockId, finished_functions: Vec, - call_stack: CallStack, + call_stack: CallStackId, error_types: BTreeMap, } @@ -53,7 +54,7 @@ impl FunctionBuilder { current_block: new_function.entry_block(), current_function: new_function, finished_functions: Vec::new(), - call_stack: CallStack::new(), + call_stack: CallStackId::root(), error_types: BTreeMap::default(), } } @@ -78,11 +79,14 @@ impl FunctionBuilder { function_id: FunctionId, runtime_type: RuntimeType, ) { + let call_stack = self.current_function.dfg.get_call_stack(self.call_stack); let mut new_function = Function::new(name, function_id); new_function.set_runtime(runtime_type); self.current_block = new_function.entry_block(); - let old_function = std::mem::replace(&mut self.current_function, new_function); + // Copy the call stack to the new function + self.call_stack = + self.current_function.dfg.call_stack_data.get_or_insert_locations(call_stack); self.finished_functions.push(old_function); } @@ -171,7 +175,7 @@ impl FunctionBuilder { instruction, block, ctrl_typevars, - self.call_stack.clone(), + self.call_stack, ) } @@ -196,17 +200,17 @@ impl FunctionBuilder { } pub(crate) fn set_location(&mut self, location: Location) -> &mut FunctionBuilder { - self.call_stack = CallStack::unit(location); + self.call_stack = self.current_function.dfg.call_stack_data.add_location_to_root(location); self } - pub(crate) fn set_call_stack(&mut self, call_stack: CallStack) -> &mut FunctionBuilder { + pub(crate) fn set_call_stack(&mut self, call_stack: CallStackId) -> &mut FunctionBuilder { self.call_stack = call_stack; self } pub(crate) fn get_call_stack(&self) -> CallStack { - self.call_stack.clone() + self.current_function.dfg.get_call_stack(self.call_stack) } /// Insert a Load instruction at the end of the current block, loading from the given address @@ -378,7 +382,7 @@ impl FunctionBuilder { destination: BasicBlockId, arguments: Vec, ) { - let call_stack = self.call_stack.clone(); + let call_stack = self.call_stack; self.terminate_block_with(TerminatorInstruction::Jmp { destination, arguments, @@ -394,7 +398,7 @@ impl FunctionBuilder { then_destination: BasicBlockId, else_destination: BasicBlockId, ) { - let call_stack = self.call_stack.clone(); + let call_stack = self.call_stack; self.terminate_block_with(TerminatorInstruction::JmpIf { condition, then_destination, @@ -405,7 +409,7 @@ impl FunctionBuilder { /// Terminate the current block with a return instruction pub(crate) fn terminate_with_return(&mut self, return_values: Vec) { - let call_stack = self.call_stack.clone(); + let call_stack = self.call_stack; self.terminate_block_with(TerminatorInstruction::Return { return_values, call_stack }); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs index a7c637dedd0..b2a923c6a51 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs @@ -1,5 +1,5 @@ use super::{ - dfg::CallStack, + call_stack::CallStackId, instruction::{InstructionId, TerminatorInstruction}, map::Id, value::ValueId, @@ -123,7 +123,7 @@ impl BasicBlock { terminator, TerminatorInstruction::Return { return_values: Vec::new(), - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }, ) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/call_stack.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/call_stack.rs new file mode 100644 index 00000000000..113609f4a11 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/call_stack.rs @@ -0,0 +1,128 @@ +use fxhash::FxHashMap; +use serde::{Deserialize, Serialize}; + +use noirc_errors::Location; + +pub(crate) type CallStack = im::Vector; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub(crate) struct CallStackId(u32); + +impl CallStackId { + pub(crate) fn root() -> Self { + Self::new(0) + } + + fn new(id: usize) -> Self { + Self(id as u32) + } + + pub(crate) fn index(&self) -> usize { + self.0 as usize + } + + pub(crate) fn is_root(&self) -> bool { + self.0 == 0 + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct LocationNode { + pub(crate) parent: Option, + pub(crate) children: Vec, + pub(crate) children_hash: FxHashMap, + pub(crate) value: Location, +} + +impl LocationNode { + pub(crate) fn new(parent: Option, value: Location) -> Self { + LocationNode { parent, children: Vec::new(), children_hash: FxHashMap::default(), value } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct CallStackHelper { + locations: Vec, +} + +impl Default for CallStackHelper { + /// Generates a new helper, with an empty location tree + fn default() -> Self { + let mut result = CallStackHelper { locations: Vec::new() }; + result.add_location_to_root(Location::dummy()); + result + } +} + +impl CallStackHelper { + /// Construct a CallStack from a CallStackId + pub(crate) fn get_call_stack(&self, mut call_stack: CallStackId) -> CallStack { + let mut result = im::Vector::new(); + while let Some(parent) = self.locations[call_stack.index()].parent { + result.push_back(self.locations[call_stack.index()].value); + call_stack = parent; + } + result + } + + /// Returns a new CallStackId which extends the call_stack with the provided call_stack. + pub(crate) fn extend_call_stack( + &mut self, + mut call_stack: CallStackId, + locations: &CallStack, + ) -> CallStackId { + for location in locations { + call_stack = self.add_child(call_stack, *location); + } + call_stack + } + + /// Adds a location to the call stack + pub(crate) fn add_child(&mut self, call_stack: CallStackId, location: Location) -> CallStackId { + let key = fxhash::hash64(&location); + if let Some(result) = self.locations[call_stack.index()].children_hash.get(&key) { + if self.locations[result.index()].value == location { + return *result; + } + } + let new_location = LocationNode::new(Some(call_stack), location); + let key = fxhash::hash64(&new_location.value); + self.locations.push(new_location); + let new_location_id = CallStackId::new(self.locations.len() - 1); + + self.locations[call_stack.index()].children.push(new_location_id); + self.locations[call_stack.index()].children_hash.insert(key, new_location_id); + new_location_id + } + + /// Retrieve the CallStackId corresponding to call_stack with the last 'len' locations removed. + pub(crate) fn unwind_call_stack( + &self, + mut call_stack: CallStackId, + mut len: usize, + ) -> CallStackId { + while len > 0 { + if let Some(parent) = self.locations[call_stack.index()].parent { + len -= 1; + call_stack = parent; + } else { + break; + } + } + call_stack + } + + pub(crate) fn add_location_to_root(&mut self, location: Location) -> CallStackId { + if self.locations.is_empty() { + self.locations.push(LocationNode::new(None, location)); + CallStackId::root() + } else { + self.add_child(CallStackId::root(), location) + } + } + + /// Get (or create) a CallStackId corresponding to the given locations + pub(crate) fn get_or_insert_locations(&mut self, locations: CallStack) -> CallStackId { + self.extend_call_stack(CallStackId::root(), &locations) + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/cfg.rs index 2268e6b2191..788b1a7d302 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/cfg.rs @@ -128,7 +128,7 @@ impl ControlFlowGraph { #[cfg(test)] mod tests { use crate::ssa::ir::{ - dfg::CallStack, instruction::TerminatorInstruction, map::Id, types::Type, + call_stack::CallStackId, instruction::TerminatorInstruction, map::Id, types::Type, }; use super::{super::function::Function, ControlFlowGraph}; @@ -140,7 +140,7 @@ mod tests { let block_id = func.entry_block(); func.dfg[block_id].set_terminator(TerminatorInstruction::Return { return_values: vec![], - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); ControlFlowGraph::with_function(&func); @@ -168,17 +168,17 @@ mod tests { condition: cond, then_destination: block2_id, else_destination: block1_id, - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); func.dfg[block1_id].set_terminator(TerminatorInstruction::JmpIf { condition: cond, then_destination: block1_id, else_destination: block2_id, - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); func.dfg[block2_id].set_terminator(TerminatorInstruction::Return { return_values: vec![], - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); let mut cfg = ControlFlowGraph::with_function(&func); @@ -226,18 +226,18 @@ mod tests { let ret_block_id = func.dfg.make_block(); func.dfg[ret_block_id].set_terminator(TerminatorInstruction::Return { return_values: vec![], - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); func.dfg[block2_id].set_terminator(TerminatorInstruction::Jmp { destination: ret_block_id, arguments: vec![], - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); func.dfg[block0_id].set_terminator(TerminatorInstruction::JmpIf { condition: cond, then_destination: block1_id, else_destination: ret_block_id, - call_stack: CallStack::new(), + call_stack: CallStackId::root(), }); // Recompute new and changed blocks diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 7546cba19d8..72ba369f98c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -4,6 +4,7 @@ use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyR use super::{ basic_block::{BasicBlock, BasicBlockId}, + call_stack::{CallStack, CallStackHelper, CallStackId}, function::FunctionId, instruction::{ Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction, @@ -91,14 +92,14 @@ pub(crate) struct DataFlowGraph { /// Instructions inserted by internal SSA passes that don't correspond to user code /// may not have a corresponding location. #[serde(skip)] - locations: HashMap, + locations: HashMap, + + pub(crate) call_stack_data: CallStackHelper, #[serde(skip)] pub(crate) data_bus: DataBus, } -pub(crate) type CallStack = super::list::List; - impl DataFlowGraph { /// Creates a new basic block with no parameters. /// After being created, the block is unreachable in the current function @@ -170,9 +171,9 @@ impl DataFlowGraph { instruction: Instruction, block: BasicBlockId, ctrl_typevars: Option>, - call_stack: CallStack, + call_stack: CallStackId, ) -> InsertInstructionResult { - match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { + match instruction.simplify(self, block, ctrl_typevars.clone(), call_stack) { SimplifyResult::SimplifiedTo(simplification) => { InsertInstructionResult::SimplifiedTo(simplification) } @@ -200,7 +201,7 @@ impl DataFlowGraph { for instruction in instructions { let id = self.make_instruction(instruction, ctrl_typevars.clone()); self.blocks[block].insert_instruction(id); - self.locations.insert(id, call_stack.clone()); + self.locations.insert(id, call_stack); last_id = Some(id); } @@ -486,21 +487,44 @@ impl DataFlowGraph { destination.set_terminator(terminator); } - pub(crate) fn get_call_stack(&self, instruction: InstructionId) -> CallStack { + pub(crate) fn get_instruction_call_stack(&self, instruction: InstructionId) -> CallStack { + let call_stack = self.get_instruction_call_stack_id(instruction); + self.call_stack_data.get_call_stack(call_stack) + } + + pub(crate) fn get_instruction_call_stack_id(&self, instruction: InstructionId) -> CallStackId { self.locations.get(&instruction).cloned().unwrap_or_default() } - pub(crate) fn add_location(&mut self, instruction: InstructionId, location: Location) { - self.locations.entry(instruction).or_default().push_back(location); + pub(crate) fn add_location_to_instruction( + &mut self, + instruction: InstructionId, + location: Location, + ) { + let call_stack = self.locations.entry(instruction).or_default(); + *call_stack = self.call_stack_data.add_child(*call_stack, location); + } + + pub(crate) fn get_call_stack(&self, call_stack: CallStackId) -> CallStack { + self.call_stack_data.get_call_stack(call_stack) } pub(crate) fn get_value_call_stack(&self, value: ValueId) -> CallStack { match &self.values[self.resolve(value)] { - Value::Instruction { instruction, .. } => self.get_call_stack(*instruction), + Value::Instruction { instruction, .. } => self.get_instruction_call_stack(*instruction), _ => CallStack::new(), } } + pub(crate) fn get_value_call_stack_id(&self, value: ValueId) -> CallStackId { + match &self.values[self.resolve(value)] { + Value::Instruction { instruction, .. } => { + self.get_instruction_call_stack_id(*instruction) + } + _ => CallStackId::root(), + } + } + /// True if the given ValueId refers to a (recursively) constant value pub(crate) fn is_constant(&self, argument: ValueId) -> bool { match &self[self.resolve(argument)] { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs index 504eecf4801..ff54bf3b6ed 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -272,8 +272,8 @@ mod tests { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, dfg::CallStack, dom::DominatorTree, function::Function, - instruction::TerminatorInstruction, map::Id, types::Type, + basic_block::BasicBlockId, call_stack::CallStackId, dom::DominatorTree, + function::Function, instruction::TerminatorInstruction, map::Id, types::Type, }, }; @@ -284,7 +284,10 @@ mod tests { let block0_id = func.entry_block(); func.dfg.set_block_terminator( block0_id, - TerminatorInstruction::Return { return_values: vec![], call_stack: CallStack::new() }, + TerminatorInstruction::Return { + return_values: vec![], + call_stack: CallStackId::root(), + }, ); let mut dom_tree = DominatorTree::with_function(&func); assert!(dom_tree.dominates(block0_id, block0_id)); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index 9ae0839c75c..9e4557e06a6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -4,7 +4,8 @@ use crate::ssa::ir::types::Type; use super::{ basic_block::BasicBlockId, - dfg::{CallStack, InsertInstructionResult}, + call_stack::CallStackId, + dfg::InsertInstructionResult, function::Function, instruction::{Instruction, InstructionId}, value::ValueId, @@ -72,10 +73,10 @@ impl<'f> FunctionInserter<'f> { } /// Get an instruction and make sure all the values in it are freshly resolved. - pub(crate) fn map_instruction(&mut self, id: InstructionId) -> (Instruction, CallStack) { + pub(crate) fn map_instruction(&mut self, id: InstructionId) -> (Instruction, CallStackId) { let mut instruction = self.function.dfg[id].clone(); instruction.map_values_mut(|id| self.resolve(id)); - (instruction, self.function.dfg.get_call_stack(id)) + (instruction, self.function.dfg.get_instruction_call_stack_id(id)) } /// Maps a terminator in place, replacing any ValueId in the terminator with the @@ -114,7 +115,7 @@ impl<'f> FunctionInserter<'f> { instruction: Instruction, id: InstructionId, mut block: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, ) -> InsertInstructionResult { let results = self.function.dfg.instruction_results(id); let results = vecmap(results, |id| self.function.dfg.resolve(*id)); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 346ac3ac11b..3072bb1d72d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -14,7 +14,8 @@ use crate::ssa::{ir::function::RuntimeType, opt::flatten_cfg::value_merger::Valu use super::{ basic_block::BasicBlockId, - dfg::{CallStack, DataFlowGraph}, + call_stack::CallStackId, + dfg::DataFlowGraph, function::Function, map::Id, types::{NumericType, Type}, @@ -808,7 +809,7 @@ impl Instruction { dfg: &mut DataFlowGraph, block: BasicBlockId, ctrl_typevars: Option>, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { use SimplifyResult::*; match self { @@ -865,7 +866,7 @@ impl Instruction { instruction, block, Option::None, - call_stack.clone(), + call_stack, ); return SimplifiedTo(new_array.first()); } @@ -1210,7 +1211,7 @@ pub(crate) enum TerminatorInstruction { condition: ValueId, then_destination: BasicBlockId, else_destination: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, }, /// Unconditional Jump @@ -1218,7 +1219,7 @@ pub(crate) enum TerminatorInstruction { /// Jumps to specified `destination` with `arguments`. /// The CallStack here is expected to be used to issue an error when the start range of /// a for loop cannot be deduced at compile-time. - Jmp { destination: BasicBlockId, arguments: Vec, call_stack: CallStack }, + Jmp { destination: BasicBlockId, arguments: Vec, call_stack: CallStackId }, /// Return from the current function with the given return values. /// @@ -1227,7 +1228,7 @@ pub(crate) enum TerminatorInstruction { /// unconditionally jump to a single exit block with the return values /// as the block arguments. Then the exit block can terminate in a return /// instruction returning these values. - Return { return_values: Vec, call_stack: CallStack }, + Return { return_values: Vec, call_stack: CallStackId }, } impl TerminatorInstruction { @@ -1242,16 +1243,16 @@ impl TerminatorInstruction { condition: f(*condition), then_destination: *then_destination, else_destination: *else_destination, - call_stack: call_stack.clone(), + call_stack: *call_stack, }, Jmp { destination, arguments, call_stack } => Jmp { destination: *destination, arguments: vecmap(arguments, |value| f(*value)), - call_stack: call_stack.clone(), + call_stack: *call_stack, }, Return { return_values, call_stack } => Return { return_values: vecmap(return_values, |value| f(*value)), - call_stack: call_stack.clone(), + call_stack: *call_stack, }, } } @@ -1311,11 +1312,19 @@ impl TerminatorInstruction { } } - pub(crate) fn call_stack(&self) -> CallStack { + pub(crate) fn call_stack(&self) -> CallStackId { match self { TerminatorInstruction::JmpIf { call_stack, .. } | TerminatorInstruction::Jmp { call_stack, .. } - | TerminatorInstruction::Return { call_stack, .. } => call_stack.clone(), + | TerminatorInstruction::Return { call_stack, .. } => *call_stack, + } + } + + pub(crate) fn set_call_stack(&mut self, new_call_stack: CallStackId) { + match self { + TerminatorInstruction::JmpIf { call_stack, .. } + | TerminatorInstruction::Jmp { call_stack, .. } + | TerminatorInstruction::Return { call_stack, .. } => *call_stack = new_call_stack, } } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 02be0910a13..fa7d314ff33 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -12,7 +12,8 @@ use num_bigint::BigUint; use crate::ssa::{ ir::{ basic_block::BasicBlockId, - dfg::{CallStack, DataFlowGraph}, + call_stack::CallStackId, + dfg::DataFlowGraph, instruction::Intrinsic, map::Id, types::{NumericType, Type}, @@ -38,7 +39,7 @@ pub(super) fn simplify_call( dfg: &mut DataFlowGraph, block: BasicBlockId, ctrl_typevars: Option>, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { let intrinsic = match &dfg[func] { Value::Intrinsic(intrinsic) => *intrinsic, @@ -148,14 +149,7 @@ pub(super) fn simplify_call( return SimplifyResult::SimplifiedToMultiple(vec![new_slice_length, new_slice]); } - simplify_slice_push_back( - slice, - element_type, - arguments, - dfg, - block, - call_stack.clone(), - ) + simplify_slice_push_back(slice, element_type, arguments, dfg, block, call_stack) } else { SimplifyResult::None } @@ -185,7 +179,7 @@ pub(super) fn simplify_call( let slice = dfg.get_array_constant(arguments[1]); if let Some((_, typ)) = slice { - simplify_slice_pop_back(typ, arguments, dfg, block, call_stack.clone()) + simplify_slice_pop_back(typ, arguments, dfg, block, call_stack) } else { SimplifyResult::None } @@ -354,7 +348,7 @@ pub(super) fn simplify_call( truncate, block, Some(vec![incoming_type]), - call_stack.clone(), + call_stack, ) .first(); @@ -412,7 +406,7 @@ fn update_slice_length( ) -> ValueId { let one = dfg.make_constant(FieldElement::one(), NumericType::length_type()); let instruction = Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }); - let call_stack = dfg.get_value_call_stack(slice_len); + let call_stack = dfg.get_value_call_stack_id(slice_len); dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() } @@ -422,23 +416,18 @@ fn simplify_slice_push_back( arguments: &[ValueId], dfg: &mut DataFlowGraph, block: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, ) -> SimplifyResult { // The capacity must be an integer so that we can compare it against the slice length let capacity = dfg.make_constant((slice.len() as u128).into(), NumericType::length_type()); let len_equals_capacity_instr = Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Eq, rhs: capacity }); let len_equals_capacity = dfg - .insert_instruction_and_results(len_equals_capacity_instr, block, None, call_stack.clone()) + .insert_instruction_and_results(len_equals_capacity_instr, block, None, call_stack) .first(); let len_not_equals_capacity_instr = Instruction::Not(len_equals_capacity); let len_not_equals_capacity = dfg - .insert_instruction_and_results( - len_not_equals_capacity_instr, - block, - None, - call_stack.clone(), - ) + .insert_instruction_and_results(len_not_equals_capacity_instr, block, None, call_stack) .first(); let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add, block); @@ -448,7 +437,7 @@ fn simplify_slice_push_back( } let slice_size = slice.len() as u32; let element_size = element_type.element_size() as u32; - let new_slice = make_array(dfg, slice, element_type, block, &call_stack); + let new_slice = make_array(dfg, slice, element_type, block, call_stack); let set_last_slice_value_instr = Instruction::ArraySet { array: new_slice, @@ -458,7 +447,7 @@ fn simplify_slice_push_back( }; let set_last_slice_value = dfg - .insert_instruction_and_results(set_last_slice_value_instr, block, None, call_stack.clone()) + .insert_instruction_and_results(set_last_slice_value_instr, block, None, call_stack) .first(); let mut slice_sizes = HashMap::default(); @@ -484,7 +473,7 @@ fn simplify_slice_pop_back( arguments: &[ValueId], dfg: &mut DataFlowGraph, block: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, ) -> SimplifyResult { let element_types = slice_type.element_types(); let element_count = element_types.len(); @@ -495,9 +484,8 @@ fn simplify_slice_pop_back( let element_size = dfg.make_constant((element_count as u128).into(), NumericType::length_type()); let flattened_len_instr = Instruction::binary(BinaryOp::Mul, arguments[0], element_size); - let mut flattened_len = dfg - .insert_instruction_and_results(flattened_len_instr, block, None, call_stack.clone()) - .first(); + let mut flattened_len = + dfg.insert_instruction_and_results(flattened_len_instr, block, None, call_stack).first(); flattened_len = update_slice_length(flattened_len, dfg, BinaryOp::Sub, block); // We must pop multiple elements in the case of a slice of tuples @@ -508,12 +496,7 @@ fn simplify_slice_pop_back( let element_type = Some(vec![element_type.clone()]); let get_last_elem = dfg - .insert_instruction_and_results( - get_last_elem_instr, - block, - element_type, - call_stack.clone(), - ) + .insert_instruction_and_results(get_last_elem_instr, block, element_type, call_stack) .first(); results.push_front(get_last_elem); @@ -533,7 +516,7 @@ fn simplify_black_box_func( arguments: &[ValueId], dfg: &mut DataFlowGraph, block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { cfg_if::cfg_if! { if #[cfg(feature = "bn254")] { @@ -632,7 +615,7 @@ fn make_constant_array( results: impl Iterator, typ: NumericType, block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> ValueId { let result_constants: im::Vector<_> = results.map(|element| dfg.make_constant(element, typ)).collect(); @@ -646,10 +629,9 @@ fn make_array( elements: im::Vector, typ: Type, block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> ValueId { let instruction = Instruction::MakeArray { elements, typ }; - let call_stack = call_stack.clone(); dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() } @@ -706,7 +688,7 @@ fn simplify_hash( arguments: &[ValueId], hash_function: fn(&[u8]) -> Result<[u8; 32], BlackBoxResolutionError>, block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { match dfg.get_array_constant(arguments[0]) { Some((input, _)) if array_is_constant(dfg, &input) => { @@ -778,7 +760,7 @@ fn simplify_derive_generators( arguments: &[ValueId], num_generators: u32, block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { if arguments.len() == 2 { let domain_separator_string = dfg.get_array_constant(arguments[0]); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index a5de98cec7f..ffacf6fe8b5 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -2,11 +2,12 @@ use std::sync::Arc; use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; +use crate::ssa::ir::call_stack::CallStackId; use crate::ssa::ir::instruction::BlackBoxFunc; use crate::ssa::ir::types::NumericType; use crate::ssa::ir::{ basic_block::BasicBlockId, - dfg::{CallStack, DataFlowGraph}, + dfg::DataFlowGraph, instruction::{Instruction, Intrinsic, SimplifyResult}, types::Type, value::ValueId, @@ -19,7 +20,7 @@ pub(super) fn simplify_ec_add( solver: impl BlackBoxFunctionSolver, arguments: &[ValueId], block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { match ( dfg.get_numeric_constant(arguments[0]), @@ -58,7 +59,7 @@ pub(super) fn simplify_ec_add( let elements = im::vector![result_x, result_y, result_is_infinity]; let instruction = Instruction::MakeArray { elements, typ }; let result_array = - dfg.insert_instruction_and_results(instruction, block, None, call_stack.clone()); + dfg.insert_instruction_and_results(instruction, block, None, call_stack); SimplifyResult::SimplifiedTo(result_array.first()) } @@ -71,7 +72,7 @@ pub(super) fn simplify_msm( solver: impl BlackBoxFunctionSolver, arguments: &[ValueId], block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { let mut is_constant; @@ -152,12 +153,8 @@ pub(super) fn simplify_msm( let elements = im::vector![result_x, result_y, result_is_infinity]; let typ = Type::Array(Arc::new(vec![Type::field()]), 3); let instruction = Instruction::MakeArray { elements, typ }; - let result_array = dfg.insert_instruction_and_results( - instruction, - block, - None, - call_stack.clone(), - ); + let result_array = + dfg.insert_instruction_and_results(instruction, block, None, call_stack); return SimplifyResult::SimplifiedTo(result_array.first()); } @@ -185,13 +182,12 @@ pub(super) fn simplify_msm( // Construct the simplified MSM expression let typ = Type::Array(Arc::new(vec![Type::field()]), var_scalars.len() as u32); let scalars = Instruction::MakeArray { elements: var_scalars.into(), typ }; - let scalars = dfg - .insert_instruction_and_results(scalars, block, None, call_stack.clone()) - .first(); + let scalars = + dfg.insert_instruction_and_results(scalars, block, None, call_stack).first(); let typ = Type::Array(Arc::new(vec![Type::field()]), var_points.len() as u32); let points = Instruction::MakeArray { elements: var_points.into(), typ }; let points = - dfg.insert_instruction_and_results(points, block, None, call_stack.clone()).first(); + dfg.insert_instruction_and_results(points, block, None, call_stack).first(); let msm = dfg.import_intrinsic(Intrinsic::BlackBox(BlackBoxFunc::MultiScalarMul)); SimplifyResult::SimplifiedToInstruction(Instruction::Call { func: msm, @@ -207,7 +203,7 @@ pub(super) fn simplify_poseidon2_permutation( solver: impl BlackBoxFunctionSolver, arguments: &[ValueId], block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { (Some((state, _)), Some(state_length)) if array_is_constant(dfg, &state) => { @@ -242,7 +238,7 @@ pub(super) fn simplify_hash( arguments: &[ValueId], hash_function: fn(&[u8]) -> Result<[u8; 32], BlackBoxResolutionError>, block: BasicBlockId, - call_stack: &CallStack, + call_stack: CallStackId, ) -> SimplifyResult { match dfg.get_array_constant(arguments[0]) { Some((input, _)) if array_is_constant(dfg, &input) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/mod.rs index 89ba22e8b79..88e0d8900db 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod basic_block; +pub(crate) mod call_stack; pub(crate) mod cfg; pub(crate) mod dfg; pub(crate) mod dom; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs index 348c78683a0..6936c7ad542 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs @@ -92,7 +92,7 @@ fn evaluate_assert_constant( if arguments.iter().all(|arg| function.dfg.is_constant(*arg)) { Ok(false) } else { - let call_stack = function.dfg.get_call_stack(instruction); + let call_stack = function.dfg.get_instruction_call_stack(instruction); Err(RuntimeError::AssertConstantFailed { call_stack }) } } @@ -113,14 +113,14 @@ fn evaluate_static_assert( } if !function.dfg.is_constant(arguments[1]) { - let call_stack = function.dfg.get_call_stack(instruction); + let call_stack = function.dfg.get_instruction_call_stack(instruction); return Err(RuntimeError::StaticAssertDynamicMessage { call_stack }); } if function.dfg.is_constant_true(arguments[0]) { Ok(false) } else { - let call_stack = function.dfg.get_call_stack(instruction); + let call_stack = function.dfg.get_instruction_call_stack(instruction); if function.dfg.is_constant(arguments[0]) { Err(RuntimeError::StaticAssertFailed { call_stack }) } else { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index faa0594f3f0..c81a557178b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -317,7 +317,7 @@ impl<'brillig> Context<'brillig> { if matches!(instruction, Instruction::MakeArray { .. }) { let value = *cached.last().unwrap(); let inc_rc = Instruction::IncrementRc { value }; - let call_stack = dfg.get_call_stack(id); + let call_stack = dfg.get_instruction_call_stack_id(id); dfg.insert_instruction_and_results(inc_rc, block, None, call_stack); } @@ -421,7 +421,7 @@ impl<'brillig> Context<'brillig> { .requires_ctrl_typevars() .then(|| vecmap(old_results, |result| dfg.type_of_value(*result))); - let call_stack = dfg.get_call_stack(id); + let call_stack = dfg.get_instruction_call_stack_id(id); let new_results = match dfg.insert_instruction_and_results(instruction, block, ctrl_typevars, call_stack) { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs index 675d7fd854e..7b38b764eab 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -6,7 +6,8 @@ use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; use crate::ssa::{ ir::{ basic_block::{BasicBlock, BasicBlockId}, - dfg::{CallStack, DataFlowGraph}, + call_stack::CallStackId, + dfg::DataFlowGraph, function::Function, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic}, post_order::PostOrder, @@ -288,7 +289,7 @@ impl Context { _ => panic!("Expected an ArrayGet or ArraySet instruction here"), }; - let call_stack = function.dfg.get_call_stack(instruction_id); + let call_stack = function.dfg.get_instruction_call_stack_id(instruction_id); let (lhs, rhs) = if function.dfg.get_numeric_constant(*index).is_some() { // If we are here it means the index is known but out of bounds. That's always an error! @@ -305,7 +306,7 @@ impl Context { Instruction::Cast(*index, length_type), block_id, None, - call_stack.clone(), + call_stack, ); let index = index.first(); @@ -315,7 +316,7 @@ impl Context { Instruction::binary(BinaryOp::Lt, index, array_length), block_id, None, - call_stack.clone(), + call_stack, ); let is_index_out_of_bounds = is_index_out_of_bounds.first(); let true_const = function.dfg.make_constant(true.into(), NumericType::bool()); @@ -328,7 +329,7 @@ impl Context { rhs, function, block_id, - call_stack.clone(), + call_stack, ); let message = Some("Index out of bounds".to_owned().into()); @@ -491,7 +492,7 @@ fn apply_side_effects( rhs: ValueId, function: &mut Function, block_id: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, ) -> (ValueId, ValueId) { // See if there's an active "enable side effects" condition let Some(condition) = side_effects_condition else { @@ -503,15 +504,14 @@ fn apply_side_effects( // Condition needs to be cast to argument type in order to multiply them together. // In our case, lhs is always a boolean. let cast = Instruction::Cast(condition, NumericType::bool()); - let casted_condition = - dfg.insert_instruction_and_results(cast, block_id, None, call_stack.clone()); + let casted_condition = dfg.insert_instruction_and_results(cast, block_id, None, call_stack); let casted_condition = casted_condition.first(); let lhs = dfg.insert_instruction_and_results( Instruction::binary(BinaryOp::Mul, lhs, casted_condition), block_id, None, - call_stack.clone(), + call_stack, ); let lhs = lhs.first(); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 046f4478eda..43420229257 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -139,8 +139,9 @@ use iter_extended::vecmap; use crate::ssa::{ ir::{ basic_block::BasicBlockId, + call_stack::CallStackId, cfg::ControlFlowGraph, - dfg::{CallStack, InsertInstructionResult}, + dfg::InsertInstructionResult, function::{Function, FunctionId, RuntimeType}, function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic, TerminatorInstruction}, @@ -234,7 +235,7 @@ struct ConditionalContext { // First block of the else branch else_branch: Option, // Call stack where the final location is that of the entire `if` expression - call_stack: CallStack, + call_stack: CallStackId, } fn flatten_function_cfg(function: &mut Function, no_predicates: &HashMap) { @@ -285,7 +286,7 @@ impl<'f> Context<'f> { if let Some(context) = self.condition_stack.last() { let previous_branch = context.else_branch.as_ref().unwrap_or(&context.then_branch); let and = Instruction::binary(BinaryOp::And, previous_branch.condition, condition); - let call_stack = self.inserter.function.dfg.get_value_call_stack(condition); + let call_stack = self.inserter.function.dfg.get_value_call_stack_id(condition); self.insert_instruction(and, call_stack) } else { condition @@ -337,7 +338,7 @@ impl<'f> Context<'f> { self.insert_instruction_with_typevars( Instruction::EnableSideEffectsIf { condition: one }, None, - CallStack::new(), + CallStackId::root(), ); self.push_instruction(*instruction); self.insert_current_side_effects_enabled(); @@ -365,13 +366,7 @@ impl<'f> Context<'f> { call_stack, } => { self.arguments_stack.push(vec![]); - self.if_start( - condition, - then_destination, - else_destination, - &block, - call_stack.clone(), - ) + self.if_start(condition, then_destination, else_destination, &block, *call_stack) } TerminatorInstruction::Jmp { destination, arguments, call_stack: _ } => { let arguments = vecmap(arguments.clone(), |value| self.inserter.resolve(value)); @@ -387,7 +382,7 @@ impl<'f> Context<'f> { } } TerminatorInstruction::Return { return_values, call_stack } => { - let call_stack = call_stack.clone(); + let call_stack = *call_stack; let return_values = vecmap(return_values.clone(), |value| self.inserter.resolve(value)); let new_return = TerminatorInstruction::Return { return_values, call_stack }; @@ -406,7 +401,7 @@ impl<'f> Context<'f> { then_destination: &BasicBlockId, else_destination: &BasicBlockId, if_entry: &BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, ) -> Vec { // manage conditions let old_condition = *condition; @@ -447,11 +442,9 @@ impl<'f> Context<'f> { cond_context.then_branch.last_block = *block; let condition_call_stack = - self.inserter.function.dfg.get_value_call_stack(cond_context.condition); - let else_condition = self.insert_instruction( - Instruction::Not(cond_context.condition), - condition_call_stack.clone(), - ); + self.inserter.function.dfg.get_value_call_stack_id(cond_context.condition); + let else_condition = + self.insert_instruction(Instruction::Not(cond_context.condition), condition_call_stack); let else_condition = self.link_condition(else_condition); let old_allocations = std::mem::take(&mut self.local_allocations); @@ -549,7 +542,7 @@ impl<'f> Context<'f> { else_condition, else_value: else_arg, }; - let call_stack = cond_context.call_stack.clone(); + let call_stack = cond_context.call_stack; self.inserter .function .dfg @@ -566,7 +559,7 @@ impl<'f> Context<'f> { /// Insert a new instruction into the function's entry block. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. - fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStack) -> ValueId { + fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStackId) -> ValueId { let block = self.inserter.function.entry_block(); self.inserter .function @@ -583,7 +576,7 @@ impl<'f> Context<'f> { &mut self, instruction: Instruction, ctrl_typevars: Option>, - call_stack: CallStack, + call_stack: CallStackId, ) -> InsertInstructionResult { let block = self.inserter.function.entry_block(); self.inserter.function.dfg.insert_instruction_and_results( @@ -607,7 +600,7 @@ impl<'f> Context<'f> { } }; let enable_side_effects = Instruction::EnableSideEffectsIf { condition }; - let call_stack = self.inserter.function.dfg.get_value_call_stack(condition); + let call_stack = self.inserter.function.dfg.get_value_call_stack_id(condition); self.insert_instruction_with_typevars(enable_side_effects, None, call_stack); } @@ -623,7 +616,7 @@ impl<'f> Context<'f> { /// any instructions in between it should be None. fn push_instruction(&mut self, id: InstructionId) { let (instruction, call_stack) = self.inserter.map_instruction(id); - let instruction = self.handle_instruction_side_effects(instruction, call_stack.clone()); + let instruction = self.handle_instruction_side_effects(instruction, call_stack); let instruction_is_allocate = matches!(&instruction, Instruction::Allocate); let entry = self.inserter.function.entry_block(); @@ -641,7 +634,7 @@ impl<'f> Context<'f> { fn handle_instruction_side_effects( &mut self, instruction: Instruction, - call_stack: CallStack, + call_stack: CallStackId, ) -> Instruction { if let Some(condition) = self.get_last_condition() { match instruction { @@ -652,11 +645,11 @@ impl<'f> Context<'f> { let argument_type = self.inserter.function.dfg.type_of_value(lhs); let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack.clone()); + let casted_condition = self.insert_instruction(cast, call_stack); let lhs = self.insert_instruction( Instruction::binary(BinaryOp::Mul, lhs, casted_condition), - call_stack.clone(), + call_stack, ); let rhs = self.insert_instruction( Instruction::binary(BinaryOp::Mul, rhs, casted_condition), @@ -675,15 +668,11 @@ impl<'f> Context<'f> { let typ = self.inserter.function.dfg.type_of_value(value); let load = Instruction::Load { address }; let previous_value = self - .insert_instruction_with_typevars( - load, - Some(vec![typ]), - call_stack.clone(), - ) + .insert_instruction_with_typevars(load, Some(vec![typ]), call_stack) .first(); - let else_condition = self - .insert_instruction(Instruction::Not(condition), call_stack.clone()); + let else_condition = + self.insert_instruction(Instruction::Not(condition), call_stack); let instruction = Instruction::IfElse { then_condition: condition, @@ -702,11 +691,11 @@ impl<'f> Context<'f> { // Condition needs to be cast to argument type in order to multiply them together. let argument_type = self.inserter.function.dfg.type_of_value(value); let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack.clone()); + let casted_condition = self.insert_instruction(cast, call_stack); let value = self.insert_instruction( Instruction::binary(BinaryOp::Mul, value, casted_condition), - call_stack.clone(), + call_stack, ); Instruction::RangeCheck { value, max_bit_size, assert_message } } @@ -717,10 +706,11 @@ impl<'f> Context<'f> { let argument_type = self.inserter.function.dfg.type_of_value(field); let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack.clone()); + + let casted_condition = self.insert_instruction(cast, call_stack); let field = self.insert_instruction( Instruction::binary(BinaryOp::Mul, field, casted_condition), - call_stack.clone(), + call_stack, ); arguments[0] = field; @@ -729,8 +719,8 @@ impl<'f> Context<'f> { } //Issue #5045: We set curve points to infinity if condition is false Value::Intrinsic(Intrinsic::BlackBox(BlackBoxFunc::EmbeddedCurveAdd)) => { - arguments[2] = self.var_or_one(arguments[2], condition, call_stack.clone()); - arguments[5] = self.var_or_one(arguments[5], condition, call_stack.clone()); + arguments[2] = self.var_or_one(arguments[2], condition, call_stack); + arguments[5] = self.var_or_one(arguments[5], condition, call_stack); Instruction::Call { func, arguments } } @@ -748,7 +738,7 @@ impl<'f> Context<'f> { let (elements, typ) = self.apply_predicate_to_msm_argument( arguments[points_array_idx], condition, - call_stack.clone(), + call_stack, ); let instruction = Instruction::MakeArray { elements, typ }; @@ -772,7 +762,7 @@ impl<'f> Context<'f> { &mut self, argument: ValueId, predicate: ValueId, - call_stack: CallStack, + call_stack: CallStackId, ) -> (im::Vector, Type) { let array_typ; let mut array_with_predicate = im::Vector::new(); @@ -780,11 +770,7 @@ impl<'f> Context<'f> { array_typ = typ.clone(); for (i, value) in array.clone().iter().enumerate() { if i % 3 == 2 { - array_with_predicate.push_back(self.var_or_one( - *value, - predicate, - call_stack.clone(), - )); + array_with_predicate.push_back(self.var_or_one(*value, predicate, call_stack)); } else { array_with_predicate.push_back(*value); } @@ -800,13 +786,10 @@ impl<'f> Context<'f> { } // Computes: if condition { var } else { 1 } - fn var_or_one(&mut self, var: ValueId, condition: ValueId, call_stack: CallStack) -> ValueId { - let field = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, var, condition), - call_stack.clone(), - ); - let not_condition = - self.insert_instruction(Instruction::Not(condition), call_stack.clone()); + fn var_or_one(&mut self, var: ValueId, condition: ValueId, call_stack: CallStackId) -> ValueId { + let field = + self.insert_instruction(Instruction::binary(BinaryOp::Mul, var, condition), call_stack); + let not_condition = self.insert_instruction(Instruction::Not(condition), call_stack); self.insert_instruction( Instruction::binary(BinaryOp::Add, field, not_condition), call_stack, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index c2b071a9c9a..df351d6c0cd 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -3,7 +3,8 @@ use fxhash::{FxHashMap as HashMap, FxHashSet}; use crate::ssa::ir::{ basic_block::BasicBlockId, - dfg::{CallStack, DataFlowGraph, InsertInstructionResult}, + call_stack::CallStackId, + dfg::{DataFlowGraph, InsertInstructionResult}, instruction::{BinaryOp, Instruction}, types::{NumericType, Type}, value::{Value, ValueId}, @@ -21,7 +22,7 @@ pub(crate) struct ValueMerger<'a> { array_set_conditionals: &'a mut HashMap, - call_stack: CallStack, + call_stack: CallStackId, } impl<'a> ValueMerger<'a> { @@ -31,7 +32,7 @@ impl<'a> ValueMerger<'a> { slice_sizes: &'a mut HashMap, array_set_conditionals: &'a mut HashMap, current_condition: Option, - call_stack: CallStack, + call_stack: CallStackId, ) -> Self { ValueMerger { dfg, @@ -106,27 +107,25 @@ impl<'a> ValueMerger<'a> { return then_value; } - let then_call_stack = dfg.get_value_call_stack(then_value); - let else_call_stack = dfg.get_value_call_stack(else_value); + let then_call_stack = dfg.get_value_call_stack_id(then_value); + let else_call_stack = dfg.get_value_call_stack_id(else_value); - let call_stack = if then_call_stack.is_empty() { else_call_stack } else { then_call_stack }; + let call_stack = if then_call_stack.is_root() { else_call_stack } else { then_call_stack }; // We must cast the bool conditions to the actual numeric type used by each value. let cast = Instruction::Cast(then_condition, then_type); let then_condition = - dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); + dfg.insert_instruction_and_results(cast, block, None, call_stack).first(); let cast = Instruction::Cast(else_condition, else_type); let else_condition = - dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); + dfg.insert_instruction_and_results(cast, block, None, call_stack).first(); let mul = Instruction::binary(BinaryOp::Mul, then_condition, then_value); - let then_value = - dfg.insert_instruction_and_results(mul, block, None, call_stack.clone()).first(); + let then_value = dfg.insert_instruction_and_results(mul, block, None, call_stack).first(); let mul = Instruction::binary(BinaryOp::Mul, else_condition, else_value); - let else_value = - dfg.insert_instruction_and_results(mul, block, None, call_stack.clone()).first(); + let else_value = dfg.insert_instruction_and_results(mul, block, None, call_stack).first(); let add = Instruction::binary(BinaryOp::Add, then_value, else_value); dfg.insert_instruction_and_results(add, block, None, call_stack).first() @@ -173,12 +172,7 @@ impl<'a> ValueMerger<'a> { let mut get_element = |array, typevars| { let get = Instruction::ArrayGet { array, index }; self.dfg - .insert_instruction_and_results( - get, - self.block, - typevars, - self.call_stack.clone(), - ) + .insert_instruction_and_results(get, self.block, typevars, self.call_stack) .first() }; @@ -195,8 +189,9 @@ impl<'a> ValueMerger<'a> { } let instruction = Instruction::MakeArray { elements: merged, typ }; - let call_stack = self.call_stack.clone(); - self.dfg.insert_instruction_and_results(instruction, self.block, None, call_stack).first() + self.dfg + .insert_instruction_and_results(instruction, self.block, None, self.call_stack) + .first() } fn merge_slice_values( @@ -250,7 +245,7 @@ impl<'a> ValueMerger<'a> { get, self.block, typevars, - self.call_stack.clone(), + self.call_stack, ) .first() } @@ -274,7 +269,7 @@ impl<'a> ValueMerger<'a> { } let instruction = Instruction::MakeArray { elements: merged, typ }; - let call_stack = self.call_stack.clone(); + let call_stack = self.call_stack; self.dfg.insert_instruction_and_results(instruction, self.block, None, call_stack).first() } @@ -296,7 +291,7 @@ impl<'a> ValueMerger<'a> { } } let instruction = Instruction::MakeArray { elements: array, typ: typ.clone() }; - let call_stack = self.call_stack.clone(); + let call_stack = self.call_stack; self.dfg .insert_instruction_and_results(instruction, self.block, None, call_stack) .first() @@ -390,12 +385,7 @@ impl<'a> ValueMerger<'a> { let mut get_element = |array, typevars| { let get = Instruction::ArrayGet { array, index }; self.dfg - .insert_instruction_and_results( - get, - self.block, - typevars, - self.call_stack.clone(), - ) + .insert_instruction_and_results(get, self.block, typevars, self.call_stack) .first() }; @@ -414,12 +404,7 @@ impl<'a> ValueMerger<'a> { } fn insert_instruction(&mut self, instruction: Instruction) -> InsertInstructionResult { - self.dfg.insert_instruction_and_results( - instruction, - self.block, - None, - self.call_stack.clone(), - ) + self.dfg.insert_instruction_and_results(instruction, self.block, None, self.call_stack) } fn insert_array_set( @@ -430,12 +415,8 @@ impl<'a> ValueMerger<'a> { condition: Option, ) -> InsertInstructionResult { let instruction = Instruction::ArraySet { array, index, value, mutable: false }; - let result = self.dfg.insert_instruction_and_results( - instruction, - self.block, - None, - self.call_stack.clone(), - ); + let result = + self.dfg.insert_instruction_and_results(instruction, self.block, None, self.call_stack); if let Some(condition) = condition { let result_index = if result.len() == 1 { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 37659ec7c98..11201fc8f85 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -11,7 +11,8 @@ use crate::ssa::{ function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - dfg::{CallStack, InsertInstructionResult}, + call_stack::CallStackId, + dfg::InsertInstructionResult, function::{Function, FunctionId, RuntimeType}, instruction::{Instruction, InstructionId, TerminatorInstruction}, value::{Value, ValueId}, @@ -83,7 +84,7 @@ struct InlineContext { recursion_level: u32, builder: FunctionBuilder, - call_stack: CallStack, + call_stack: CallStackId, // The FunctionId of the entry point function we're inlining into in the old, unmodified Ssa. entry_point: FunctionId, @@ -365,7 +366,7 @@ impl InlineContext { builder, recursion_level: 0, entry_point, - call_stack: CallStack::new(), + call_stack: CallStackId::root(), inline_no_predicates_functions, functions_not_to_inline, } @@ -660,13 +661,25 @@ impl<'function> PerFunctionContext<'function> { let old_results = self.source_function.dfg.instruction_results(call_id); let arguments = vecmap(arguments, |arg| self.translate_value(*arg)); - let call_stack = self.source_function.dfg.get_call_stack(call_id); + let call_stack = self.source_function.dfg.get_instruction_call_stack(call_id); let call_stack_len = call_stack.len(); - self.context.call_stack.append(call_stack); - + let new_call_stack = self + .context + .builder + .current_function + .dfg + .call_stack_data + .extend_call_stack(self.context.call_stack, &call_stack); + + self.context.call_stack = new_call_stack; let new_results = self.context.inline_function(ssa, function, &arguments); - - self.context.call_stack.truncate(self.context.call_stack.len() - call_stack_len); + self.context.call_stack = self + .context + .builder + .current_function + .dfg + .call_stack_data + .unwind_call_stack(self.context.call_stack, call_stack_len); let new_results = InsertInstructionResult::Results(call_id, &new_results); Self::insert_new_instruction_results(&mut self.values, old_results, new_results); @@ -677,9 +690,15 @@ impl<'function> PerFunctionContext<'function> { fn push_instruction(&mut self, id: InstructionId) { let instruction = self.source_function.dfg[id].map_values(|id| self.translate_value(id)); - let mut call_stack = self.context.call_stack.clone(); - call_stack.append(self.source_function.dfg.get_call_stack(id)); - + let mut call_stack = self.context.call_stack; + let source_call_stack = self.source_function.dfg.get_instruction_call_stack(id); + call_stack = self + .context + .builder + .current_function + .dfg + .call_stack_data + .extend_call_stack(call_stack, &source_call_stack); let results = self.source_function.dfg.instruction_results(id); let results = vecmap(results, |id| self.source_function.dfg.resolve(*id)); @@ -736,8 +755,14 @@ impl<'function> PerFunctionContext<'function> { let destination = self.translate_block(*destination, block_queue); let arguments = vecmap(arguments, |arg| self.translate_value(*arg)); - let mut new_call_stack = self.context.call_stack.clone(); - new_call_stack.append(call_stack.clone()); + let call_stack = self.source_function.dfg.get_call_stack(*call_stack); + let new_call_stack = self + .context + .builder + .current_function + .dfg + .call_stack_data + .extend_call_stack(self.context.call_stack, &call_stack); self.context .builder @@ -752,9 +777,14 @@ impl<'function> PerFunctionContext<'function> { call_stack, } => { let condition = self.translate_value(*condition); - - let mut new_call_stack = self.context.call_stack.clone(); - new_call_stack.append(call_stack.clone()); + let call_stack = self.source_function.dfg.get_call_stack(*call_stack); + let new_call_stack = self + .context + .builder + .current_function + .dfg + .call_stack_data + .extend_call_stack(self.context.call_stack, &call_stack); // See if the value of the condition is known, and if so only inline the reachable // branch. This lets us inline some recursive functions without recurring forever. @@ -791,8 +821,16 @@ impl<'function> PerFunctionContext<'function> { let block_id = self.context.builder.current_block(); if self.inlining_entry { - let mut new_call_stack = self.context.call_stack.clone(); - new_call_stack.append(call_stack.clone()); + let call_stack = + self.source_function.dfg.call_stack_data.get_call_stack(*call_stack); + let new_call_stack = self + .context + .builder + .current_function + .dfg + .call_stack_data + .extend_call_stack(self.context.call_stack, &call_stack); + self.context .builder .set_call_stack(new_call_stack) diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index 7e4546083b8..44c0eb380c2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -119,7 +119,11 @@ impl<'f> LoopInvariantContext<'f> { let result = self.inserter.function.dfg.instruction_results(instruction_id)[0]; let inc_rc = Instruction::IncrementRc { value: result }; - let call_stack = self.inserter.function.dfg.get_call_stack(instruction_id); + let call_stack = self + .inserter + .function + .dfg + .get_instruction_call_stack_id(instruction_id); self.inserter .function .dfg diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index 9420fb9b4f7..63ca523bd57 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -86,7 +86,10 @@ impl Context { let instruction = old_function.dfg[old_instruction_id] .map_values(|value| self.new_ids.map_value(new_function, old_function, value)); - let call_stack = old_function.dfg.get_call_stack(old_instruction_id); + let call_stack = old_function.dfg.get_instruction_call_stack_id(old_instruction_id); + let locations = old_function.dfg.get_call_stack(call_stack); + let new_call_stack = + new_function.dfg.call_stack_data.get_or_insert_locations(locations); let old_results = old_function.dfg.instruction_results(old_instruction_id); let ctrl_typevars = instruction @@ -97,7 +100,7 @@ impl Context { instruction, new_block_id, ctrl_typevars, - call_stack, + new_call_stack, ); assert_eq!(old_results.len(), new_results.len()); @@ -114,6 +117,10 @@ impl Context { .map_values_mut(|value| self.new_ids.map_value(new_function, old_function, value)); terminator.mutate_blocks(|old_block| self.new_ids.blocks[&old_block]); + let locations = old_function.dfg.get_call_stack(terminator.call_stack()); + let new_call_stack = + new_function.dfg.call_stack_data.get_or_insert_locations(locations); + terminator.set_call_stack(new_call_stack); new_function.dfg.set_block_terminator(new_block_id, terminator); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index 872c7920a77..4c5189d8c91 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -5,7 +5,8 @@ use acvm::{acir::AcirField, FieldElement}; use crate::ssa::{ ir::{ basic_block::BasicBlockId, - dfg::{CallStack, InsertInstructionResult}, + call_stack::CallStackId, + dfg::InsertInstructionResult, function::{Function, RuntimeType}, instruction::{Binary, BinaryOp, Endian, Instruction, InstructionId, Intrinsic}, types::{NumericType, Type}, @@ -40,7 +41,7 @@ impl Function { function: self, new_instructions: Vec::new(), block, - call_stack: CallStack::default(), + call_stack: CallStackId::root(), }; context.remove_bit_shifts(); @@ -52,7 +53,7 @@ struct Context<'f> { new_instructions: Vec, block: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, } impl Context<'_> { @@ -64,7 +65,8 @@ impl Context<'_> { Instruction::Binary(Binary { lhs, rhs, operator }) if matches!(operator, BinaryOp::Shl | BinaryOp::Shr) => { - self.call_stack = self.function.dfg.get_call_stack(instruction_id).clone(); + self.call_stack = + self.function.dfg.get_instruction_call_stack_id(instruction_id); let old_result = *self.function.dfg.instruction_results(instruction_id).first().unwrap(); @@ -295,7 +297,7 @@ impl Context<'_> { instruction, self.block, ctrl_typevars, - self.call_stack.clone(), + self.call_stack, ); if let InsertInstructionResult::Results(instruction_id, _) = result { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index 45b7f9072d8..79f2354aff6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -78,7 +78,7 @@ impl Context { let typ = function.dfg.type_of_value(then_value); assert!(!matches!(typ, Type::Numeric(_))); - let call_stack = function.dfg.get_call_stack(instruction); + let call_stack = function.dfg.get_instruction_call_stack_id(instruction); let mut value_merger = ValueMerger::new( &mut function.dfg, block, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index e7f8d227d28..22fdf0a7987 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -109,7 +109,7 @@ fn check_for_constant_jmpif( }; let arguments = Vec::new(); - let call_stack = call_stack.clone(); + let call_stack = *call_stack; let jmp = TerminatorInstruction::Jmp { destination, arguments, call_stack }; function.dfg[block].set_terminator(jmp); cfg.recompute_block(function, block); @@ -223,7 +223,7 @@ fn check_for_negated_jmpif_condition( { if let Value::Instruction { instruction, .. } = function.dfg[*condition] { if let Instruction::Not(negated_condition) = function.dfg[instruction] { - let call_stack = call_stack.clone(); + let call_stack = *call_stack; let jmpif = TerminatorInstruction::JmpIf { condition: negated_condition, then_destination: *else_destination, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 1ad03982bb8..2a272236195 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -27,8 +27,9 @@ use crate::{ ssa::{ ir::{ basic_block::BasicBlockId, + call_stack::{CallStack, CallStackId}, cfg::ControlFlowGraph, - dfg::{CallStack, DataFlowGraph}, + dfg::DataFlowGraph, dom::DominatorTree, function::Function, function_inserter::{ArrayCache, FunctionInserter}, @@ -470,7 +471,7 @@ impl Loop { match context.dfg()[fresh_block].unwrap_terminator() { TerminatorInstruction::JmpIf { condition, then_destination, else_destination, call_stack } => { let condition = *condition; - let next_blocks = context.handle_jmpif(condition, *then_destination, *else_destination, call_stack.clone()); + let next_blocks = context.handle_jmpif(condition, *then_destination, *else_destination, *call_stack); // If there is only 1 next block the jmpif evaluated to a single known block. // This is the expected case and lets us know if we should loop again or not. @@ -746,10 +747,11 @@ fn get_induction_variable(function: &Function, block: BasicBlockId) -> Result Err(terminator.call_stack()), + Some(terminator) => Err(function.dfg.get_call_stack(terminator.call_stack())), None => Err(CallStack::new()), } } @@ -848,12 +850,7 @@ impl<'f> LoopIteration<'f> { then_destination, else_destination, call_stack, - } => self.handle_jmpif( - *condition, - *then_destination, - *else_destination, - call_stack.clone(), - ), + } => self.handle_jmpif(*condition, *then_destination, *else_destination, *call_stack), TerminatorInstruction::Jmp { destination, arguments, call_stack: _ } => { if self.get_original_block(*destination) == self.loop_.header { // We found the back-edge of the loop. @@ -877,7 +874,7 @@ impl<'f> LoopIteration<'f> { condition: ValueId, then_destination: BasicBlockId, else_destination: BasicBlockId, - call_stack: CallStack, + call_stack: CallStackId, ) -> Vec { let condition = self.inserter.resolve(condition); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 536f2cdb477..d3821158b80 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -91,12 +91,18 @@ pub(crate) fn generate_ssa( None, ); } + let return_call_stack = function_context + .builder + .current_function + .dfg + .call_stack_data + .add_location_to_root(return_location); let return_instruction = function_context.builder.current_function.dfg[block].unwrap_terminator_mut(); + match return_instruction { TerminatorInstruction::Return { return_values, call_stack } => { - call_stack.clear(); - call_stack.push_back(return_location); + *call_stack = return_call_stack; // replace the returned values with the return data array if let Some(return_data_bus) = return_data.databus { return_values.clear(); diff --git a/noir/noir-repo/docs/docs/getting_started/quick_start.md b/noir/noir-repo/docs/docs/getting_started/quick_start.md index c693624eb82..7deeae12fd9 100644 --- a/noir/noir-repo/docs/docs/getting_started/quick_start.md +++ b/noir/noir-repo/docs/docs/getting_started/quick_start.md @@ -13,7 +13,7 @@ The easiest way to develop with Noir is using Nargo the CLI tool. It provides yo You can use `noirup` the installation script to quickly install and update Nargo: ```bash -curl -L noirup.dev | bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup ``` @@ -32,7 +32,7 @@ You can use the `bbup` installation script to quickly install and update BB, Bar You can find the full list of proving backends compatible with Noir in Awesome Noir. ```bash -curl -L bbup.dev | bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup ``` @@ -89,7 +89,7 @@ The command also automatically compiles your Noir program if it was not already With circuit compiled and witness generated, we're ready to prove. ## Proving backend - + Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: ```sh diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.36.0/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v0.36.0/getting_started/quick_start.md index 3c10086123f..b9c2ccad286 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.36.0/getting_started/quick_start.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.36.0/getting_started/quick_start.md @@ -13,7 +13,7 @@ The easiest way to develop with Noir is using Nargo the CLI tool. It provides yo You can use `noirup` the installation script to quickly install and update Nargo: ```bash -curl -L noirup.dev | bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup ``` @@ -30,7 +30,7 @@ You can use the `bbup` installation script to quickly install and update BB, Bar You can find the full list of proving backends compatible with Noir in Awesome Noir. ```bash -curl -L bbup.dev | bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup ``` diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.37.0/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v0.37.0/getting_started/quick_start.md index 4ce48409818..73ef62f6523 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.37.0/getting_started/quick_start.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.37.0/getting_started/quick_start.md @@ -13,7 +13,7 @@ The easiest way to develop with Noir is using Nargo the CLI tool. It provides yo You can use `noirup` the installation script to quickly install and update Nargo: ```bash -curl -L noirup.dev | bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup ``` @@ -30,7 +30,7 @@ You can use the `bbup` installation script to quickly install and update BB, Bar You can find the full list of proving backends compatible with Noir in Awesome Noir. ```bash -curl -L bbup.dev | bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup ``` @@ -87,7 +87,7 @@ The command also automatically compiles your Noir program if it was not already With circuit compiled and witness generated, we're ready to prove. ## Proving backend - + Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: ```sh diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.38.0/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v0.38.0/getting_started/quick_start.md index c693624eb82..7deeae12fd9 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.38.0/getting_started/quick_start.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.38.0/getting_started/quick_start.md @@ -13,7 +13,7 @@ The easiest way to develop with Noir is using Nargo the CLI tool. It provides yo You can use `noirup` the installation script to quickly install and update Nargo: ```bash -curl -L noirup.dev | bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup ``` @@ -32,7 +32,7 @@ You can use the `bbup` installation script to quickly install and update BB, Bar You can find the full list of proving backends compatible with Noir in Awesome Noir. ```bash -curl -L bbup.dev | bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup ``` @@ -89,7 +89,7 @@ The command also automatically compiles your Noir program if it was not already With circuit compiled and witness generated, we're ready to prove. ## Proving backend - + Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: ```sh diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.39.0/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v0.39.0/getting_started/quick_start.md index c693624eb82..7deeae12fd9 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.39.0/getting_started/quick_start.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.39.0/getting_started/quick_start.md @@ -13,7 +13,7 @@ The easiest way to develop with Noir is using Nargo the CLI tool. It provides yo You can use `noirup` the installation script to quickly install and update Nargo: ```bash -curl -L noirup.dev | bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup ``` @@ -32,7 +32,7 @@ You can use the `bbup` installation script to quickly install and update BB, Bar You can find the full list of proving backends compatible with Noir in Awesome Noir. ```bash -curl -L bbup.dev | bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup ``` @@ -89,7 +89,7 @@ The command also automatically compiles your Noir program if it was not already With circuit compiled and witness generated, we're ready to prove. ## Proving backend - + Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: ```sh diff --git a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.0/getting_started/quick_start.md b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.0/getting_started/quick_start.md index c693624eb82..7deeae12fd9 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.0/getting_started/quick_start.md +++ b/noir/noir-repo/docs/versioned_docs/version-v1.0.0-beta.0/getting_started/quick_start.md @@ -13,7 +13,7 @@ The easiest way to develop with Noir is using Nargo the CLI tool. It provides yo You can use `noirup` the installation script to quickly install and update Nargo: ```bash -curl -L noirup.dev | bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup ``` @@ -32,7 +32,7 @@ You can use the `bbup` installation script to quickly install and update BB, Bar You can find the full list of proving backends compatible with Noir in Awesome Noir. ```bash -curl -L bbup.dev | bash +curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup ``` @@ -89,7 +89,7 @@ The command also automatically compiles your Noir program if it was not already With circuit compiled and witness generated, we're ready to prove. ## Proving backend - + Different proving backends may provide different tools and commands to work with Noir programs. Here Barretenberg's `bb` CLI tool is used as an example: ```sh From 5e4b46d577ebf63114a5a5a1c5b6d2947d3b2567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Mon, 16 Dec 2024 13:02:22 -0300 Subject: [PATCH 03/10] fix: always remove nullified notes (#10722) We used to remove nullified notes as we process tagged logs, but this is incomplete: it may happen that a recipient got no new logs but they still need to nullify things. I imagine we never caught this because either our tests are not comprehensive enough, of because all in all scenarios we tried there was always a new note (e.g. a transfer for less than the full balance resulted in a change note). I changed things so that the note syncing process also triggers a full search of all nullifiers for all recipients always, instead of only doing it for those that got notes. This is perhaps not ideal long-term, but it is a simple fix for the issue we currently have. --------- Co-authored-by: thunkar --- .../pxe/src/simulator_oracle/index.ts | 45 ++++++++++--------- .../simulator_oracle/simulator_oracle.test.ts | 39 ++++++---------- .../src/client/client_execution_context.ts | 2 + .../simulator/src/client/db_oracle.ts | 5 +++ .../simulator/src/client/view_data_oracle.ts | 3 ++ yarn-project/txe/src/oracle/txe_oracle.ts | 2 + 6 files changed, 50 insertions(+), 46 deletions(-) diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 160947e25c1..fdd3058ab1d 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -624,27 +624,30 @@ export class SimulatorOracle implements DBOracle { }); }); } - const nullifiedNotes: IncomingNoteDao[] = []; - const currentNotesForRecipient = await this.db.getIncomingNotes({ owner: recipient }); - const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier); - const currentBlockNumber = await this.getBlockNumber(); - const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock(currentBlockNumber, nullifiersToCheck); - - const foundNullifiers = nullifiersToCheck - .map((nullifier, i) => { - if (nullifierIndexes[i] !== undefined) { - return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock; - } - }) - .filter(nullifier => nullifier !== undefined) as InBlock[]; - - await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint()); - nullifiedNotes.forEach(noteDao => { - this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, { - contract: noteDao.contractAddress, - slot: noteDao.storageSlot, - nullifier: noteDao.siloedNullifier.toString(), + } + + public async removeNullifiedNotes(contractAddress: AztecAddress) { + for (const recipient of await this.keyStore.getAccounts()) { + const currentNotesForRecipient = await this.db.getIncomingNotes({ contractAddress, owner: recipient }); + const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier); + const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock('latest', nullifiersToCheck); + + const foundNullifiers = nullifiersToCheck + .map((nullifier, i) => { + if (nullifierIndexes[i] !== undefined) { + return { ...nullifierIndexes[i], ...{ data: nullifier } } as InBlock; + } + }) + .filter(nullifier => nullifier !== undefined) as InBlock[]; + + const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, recipient.toAddressPoint()); + nullifiedNotes.forEach(noteDao => { + this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, { + contract: noteDao.contractAddress, + slot: noteDao.storageSlot, + nullifier: noteDao.siloedNullifier.toString(), + }); }); - }); + } } } diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts index 1e88eaab363..b291362943c 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -2,11 +2,13 @@ import { type AztecNode, EncryptedLogPayload, L1NotePayload, + L2Block, Note, type TxEffect, TxHash, TxScopedL2Log, randomInBlock, + wrapInBlock, } from '@aztec/circuit-types'; import { AztecAddress, @@ -654,40 +656,27 @@ describe('Simulator oracle', () => { } }); - it('should not store nullified notes', async () => { + it('should remove nullified notes', async () => { const requests = [ new MockNoteRequest(getRandomNoteLogPayload(Fr.random(), contractAddress), 1, 1, 1, recipient.address), new MockNoteRequest(getRandomNoteLogPayload(Fr.random(), contractAddress), 6, 3, 2, recipient.address), new MockNoteRequest(getRandomNoteLogPayload(Fr.random(), contractAddress), 12, 3, 2, recipient.address), ]; - const taggedLogs = mockTaggedLogs(requests, 2); - - getIncomingNotesSpy.mockResolvedValueOnce(Promise.resolve(requests.map(request => request.snippetOfNoteDao))); - - await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator); - - expect(addNotesSpy).toHaveBeenCalledTimes(1); - expect(addNotesSpy).toHaveBeenCalledWith( - // Incoming should contain notes from requests 0, 1, 2 because in those requests we set owner address point. - [ - expect.objectContaining({ - ...requests[0].snippetOfNoteDao, - index: requests[0].indexWithinNoteHashTree, - }), - expect.objectContaining({ - ...requests[1].snippetOfNoteDao, - index: requests[1].indexWithinNoteHashTree, - }), - expect.objectContaining({ - ...requests[2].snippetOfNoteDao, - index: requests[2].indexWithinNoteHashTree, - }), - ], - recipient.address, + getIncomingNotesSpy.mockResolvedValueOnce( + Promise.resolve(requests.map(request => ({ siloedNullifier: Fr.random(), ...request.snippetOfNoteDao }))), ); + let requestedNullifier; + aztecNode.findNullifiersIndexesWithBlock.mockImplementationOnce((_blockNumber, nullifiers) => { + const block = L2Block.random(2); + requestedNullifier = wrapInBlock(nullifiers[0], block); + return Promise.resolve([wrapInBlock(1n, L2Block.random(2)), undefined, undefined]); + }); + + await simulatorOracle.removeNullifiedNotes(contractAddress); expect(removeNullifiedNotesSpy).toHaveBeenCalledTimes(1); + expect(removeNullifiedNotesSpy).toHaveBeenCalledWith([requestedNullifier], recipient.address.toAddressPoint()); }, 30_000); }); }); diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 6a9a8eff781..12e8c814a2a 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -565,5 +565,7 @@ export class ClientExecutionContext extends ViewDataOracle { for (const [recipient, taggedLogs] of taggedLogsByRecipient.entries()) { await this.db.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient)); } + + await this.db.removeNullifiedNotes(this.contractAddress); } } diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index 9472148257a..d53022e49e2 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -242,4 +242,9 @@ export interface DBOracle extends CommitmentsDB { * @param recipient - The recipient of the logs. */ processTaggedLogs(logs: TxScopedL2Log[], recipient: AztecAddress): Promise; + + /** + * Removes all of a contract's notes that have been nullified from the note database. + */ + removeNullifiedNotes(contractAddress: AztecAddress): Promise; } diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index bfd870760b1..d80b2d91182 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -314,8 +314,11 @@ export class ViewDataOracle extends TypedOracle { await this.aztecNode.getBlockNumber(), this.scopes, ); + for (const [recipient, taggedLogs] of taggedLogsByRecipient.entries()) { await this.db.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient)); } + + await this.db.removeNullifiedNotes(this.contractAddress); } } diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 03928a58a30..ac5792a5b7c 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -921,6 +921,8 @@ export class TXE implements TypedOracle { await this.simulatorOracle.processTaggedLogs(taggedLogs, AztecAddress.fromString(recipient)); } + await this.simulatorOracle.removeNullifiedNotes(this.contractAddress); + return Promise.resolve(); } From 1b1306c7dbd9d363181146e02181af4727779b42 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Mon, 16 Dec 2024 17:40:00 +0000 Subject: [PATCH 04/10] fix: cache (#10692) Things missed in #10567 --------- Co-authored-by: just-mitch <68168980+just-mitch@users.noreply.github.com> --- scripts/tmux_split_args.sh | 14 +- .../src/interfaces/prover-broker.ts | 6 - .../src/interfaces/prover-client.ts | 35 +--- yarn-project/foundation/src/config/env_var.ts | 1 - yarn-project/foundation/src/string/index.ts | 4 + yarn-project/prover-client/src/index.ts | 1 - .../src/prover-client/prover-client.ts | 13 +- .../broker_prover_facade.test.ts | 152 +++++++++++++++++ ...oker_facade.ts => broker_prover_facade.ts} | 109 ++++-------- .../caching_broker_facade.test.ts | 156 ------------------ .../src/proving_broker/prover_cache/memory.ts | 20 --- .../src/proving_broker/proving_agent.ts | 14 +- .../src/proving_broker/proving_broker.test.ts | 120 +++++++------- .../src/proving_broker/proving_broker.ts | 137 +++++++++++---- .../proving_job_controller.test.ts | 1 + .../proving_broker/proving_job_controller.ts | 25 +-- .../prover-client/src/proving_broker/rpc.ts | 1 - .../prover-client/src/test/mock_prover.ts | 9 +- yarn-project/prover-node/src/factory.ts | 6 - .../src/prover-cache/cache_manager.ts | 69 -------- .../prover-node/src/prover-cache/kv_cache.ts | 27 --- .../prover-node/src/prover-node.test.ts | 4 - yarn-project/prover-node/src/prover-node.ts | 17 +- 23 files changed, 395 insertions(+), 546 deletions(-) create mode 100644 yarn-project/prover-client/src/proving_broker/broker_prover_facade.test.ts rename yarn-project/prover-client/src/proving_broker/{caching_broker_facade.ts => broker_prover_facade.ts} (72%) delete mode 100644 yarn-project/prover-client/src/proving_broker/caching_broker_facade.test.ts delete mode 100644 yarn-project/prover-client/src/proving_broker/prover_cache/memory.ts delete mode 100644 yarn-project/prover-node/src/prover-cache/cache_manager.ts delete mode 100644 yarn-project/prover-node/src/prover-cache/kv_cache.ts diff --git a/scripts/tmux_split_args.sh b/scripts/tmux_split_args.sh index 25b3a5a4b6f..ef69695682d 100755 --- a/scripts/tmux_split_args.sh +++ b/scripts/tmux_split_args.sh @@ -23,23 +23,29 @@ tmux new-session -d -s "$session_name" -e LOG_LEVEL=${LOG_LEVEL:-"debug"} \ -e OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-} \ -e LOG_JSON=${LOG_JSON:-} +echo "DONE" + shift 1 commands=("$@") # Set pane-border-status to top and pane-border-format to display pane title tmux set-option -t "$session_name" pane-border-status top tmux set-option -t "$session_name" pane-border-format "#{pane_title}" +base_index=$(tmux show-options -g base-index 2>/dev/null | awk '{print $2}') +base_index=${base_index:-0} + +echo "base_index=$base_index" # Create the necessary number of panes and set titles num_commands=${#commands[@]} for ((i=0; i; - /** - * Cleans up after a job has completed. Throws if the job is in-progress - * @param id - The ID of the job to cancel - */ - cleanUpProvingJobState(id: ProvingJobId): Promise; - /** * Returns the current status fof the proving job * @param id - The ID of the job to get the status of diff --git a/yarn-project/circuit-types/src/interfaces/prover-client.ts b/yarn-project/circuit-types/src/interfaces/prover-client.ts index 5dd392d95c6..384bf8331ec 100644 --- a/yarn-project/circuit-types/src/interfaces/prover-client.ts +++ b/yarn-project/circuit-types/src/interfaces/prover-client.ts @@ -7,7 +7,6 @@ import { z } from 'zod'; import { type TxHash } from '../tx/tx_hash.js'; import { type EpochProver } from './epoch-prover.js'; import { type ProvingJobConsumer } from './prover-broker.js'; -import { type ProvingJobStatus } from './proving-job.js'; export type ActualProverConfig = { /** Whether to construct real proofs */ @@ -24,9 +23,6 @@ export type ProverConfig = ActualProverConfig & { nodeUrl?: string; /** Identifier of the prover */ proverId: Fr; - /** Where to store temporary data */ - cacheDir?: string; - proverAgentCount: number; }; @@ -35,7 +31,6 @@ export const ProverConfigSchema = z.object({ realProofs: z.boolean(), proverId: schemas.Fr, proverTestDelayMs: z.number(), - cacheDir: z.string().optional(), proverAgentCount: z.number(), }) satisfies ZodFor; @@ -60,11 +55,6 @@ export const proverConfigMappings: ConfigMappingsType = { description: 'Artificial delay to introduce to all operations to the test prover.', ...numberConfigHelper(0), }, - cacheDir: { - env: 'PROVER_CACHE_DIR', - description: 'Where to store cache data generated while proving', - defaultValue: '/tmp/aztec-prover', - }, proverAgentCount: { env: 'PROVER_AGENT_COUNT', description: 'The number of prover agents to start', @@ -76,35 +66,12 @@ function parseProverId(str: string) { return Fr.fromHexString(str.startsWith('0x') ? str : Buffer.from(str, 'utf8').toString('hex')); } -/** - * A database where the proving orchestrator can store intermediate results - */ -export interface ProverCache { - /** - * Saves the status of a proving job - * @param jobId - The job ID - * @param status - The status of the proof - */ - setProvingJobStatus(jobId: string, status: ProvingJobStatus): Promise; - - /** - * Retrieves the status of a proving job (if known) - * @param jobId - The job ID - */ - getProvingJobStatus(jobId: string): Promise; - - /** - * Closes the cache - */ - close(): Promise; -} - /** * The interface to the prover client. * Provides the ability to generate proofs and build rollups. */ export interface EpochProverManager { - createEpochProver(cache?: ProverCache): EpochProver; + createEpochProver(): EpochProver; start(): Promise; diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index ea13f2f9711..b1d0c8ce21f 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -125,7 +125,6 @@ export type EnvVar = | 'PROVER_REAL_PROOFS' | 'PROVER_REQUIRED_CONFIRMATIONS' | 'PROVER_TEST_DELAY_MS' - | 'PROVER_CACHE_DIR' | 'PXE_L2_STARTING_BLOCK' | 'PXE_PROVER_ENABLED' | 'QUOTE_PROVIDER_BASIS_POINT_FEE' diff --git a/yarn-project/foundation/src/string/index.ts b/yarn-project/foundation/src/string/index.ts index 1b85173fc1a..c6d0d4d8fc9 100644 --- a/yarn-project/foundation/src/string/index.ts +++ b/yarn-project/foundation/src/string/index.ts @@ -25,3 +25,7 @@ export function pluralize(str: string, count: number | bigint, plural?: string): export function count(count: number | bigint, str: string, plural?: string): string { return `${count} ${pluralize(str, count, plural)}`; } + +export function truncate(str: string, length: number = 64): string { + return str.length > length ? str.slice(0, length) + '...' : str; +} diff --git a/yarn-project/prover-client/src/index.ts b/yarn-project/prover-client/src/index.ts index 822b565f54a..60feee5fa19 100644 --- a/yarn-project/prover-client/src/index.ts +++ b/yarn-project/prover-client/src/index.ts @@ -2,4 +2,3 @@ export { EpochProverManager } from '@aztec/circuit-types'; export * from './prover-client/index.js'; export * from './config.js'; -export * from './proving_broker/prover_cache/memory.js'; diff --git a/yarn-project/prover-client/src/prover-client/prover-client.ts b/yarn-project/prover-client/src/prover-client/prover-client.ts index b5d81a34905..115b2a9f784 100644 --- a/yarn-project/prover-client/src/prover-client/prover-client.ts +++ b/yarn-project/prover-client/src/prover-client/prover-client.ts @@ -4,7 +4,6 @@ import { type EpochProver, type EpochProverManager, type ForkMerkleTreeOperations, - type ProverCache, type ProvingJobBroker, type ProvingJobConsumer, type ProvingJobProducer, @@ -16,13 +15,10 @@ import { createLogger } from '@aztec/foundation/log'; import { NativeACVMSimulator } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; -import { join } from 'path'; - import { type ProverClientConfig } from '../config.js'; import { ProvingOrchestrator } from '../orchestrator/orchestrator.js'; -import { CachingBrokerFacade } from '../proving_broker/caching_broker_facade.js'; +import { BrokerCircuitProverFacade } from '../proving_broker/broker_prover_facade.js'; import { InlineProofStore } from '../proving_broker/proof_store.js'; -import { InMemoryProverCache } from '../proving_broker/prover_cache/memory.js'; import { ProvingAgent } from '../proving_broker/proving_agent.js'; /** Manages proving of epochs by orchestrating the proving of individual blocks relying on a pool of prover agents. */ @@ -30,8 +26,6 @@ export class ProverClient implements EpochProverManager { private running = false; private agents: ProvingAgent[] = []; - private cacheDir?: string; - private constructor( private config: ProverClientConfig, private worldState: ForkMerkleTreeOperations, @@ -42,13 +36,12 @@ export class ProverClient implements EpochProverManager { ) { // TODO(palla/prover-node): Cache the paddingTx here, and not in each proving orchestrator, // so it can be reused across multiple ones and not recomputed every time. - this.cacheDir = this.config.cacheDir ? join(this.config.cacheDir, `tx_prover_${this.config.proverId}`) : undefined; } - public createEpochProver(cache: ProverCache = new InMemoryProverCache()): EpochProver { + public createEpochProver(): EpochProver { return new ProvingOrchestrator( this.worldState, - new CachingBrokerFacade(this.orchestratorClient, cache), + new BrokerCircuitProverFacade(this.orchestratorClient), this.telemetry, this.config.proverId, ); diff --git a/yarn-project/prover-client/src/proving_broker/broker_prover_facade.test.ts b/yarn-project/prover-client/src/proving_broker/broker_prover_facade.test.ts new file mode 100644 index 00000000000..13c09f09011 --- /dev/null +++ b/yarn-project/prover-client/src/proving_broker/broker_prover_facade.test.ts @@ -0,0 +1,152 @@ +import { makePublicInputsAndRecursiveProof } from '@aztec/circuit-types'; +import { RECURSIVE_PROOF_LENGTH, VerificationKeyData, makeRecursiveProof } from '@aztec/circuits.js'; +import { makeBaseParityInputs, makeParityPublicInputs } from '@aztec/circuits.js/testing'; +import { promiseWithResolvers } from '@aztec/foundation/promise'; +import { sleep } from '@aztec/foundation/sleep'; + +import { jest } from '@jest/globals'; + +import { MockProver, TestBroker } from '../test/mock_prover.js'; +import { BrokerCircuitProverFacade } from './broker_prover_facade.js'; +import { InlineProofStore } from './proof_store.js'; + +describe('BrokerCircuitProverFacade', () => { + let facade: BrokerCircuitProverFacade; + let proofStore: InlineProofStore; + let broker: TestBroker; + let prover: MockProver; + let agentPollInterval: number; + + beforeEach(async () => { + proofStore = new InlineProofStore(); + prover = new MockProver(); + agentPollInterval = 100; + broker = new TestBroker(2, prover, proofStore, agentPollInterval); + facade = new BrokerCircuitProverFacade(broker, proofStore); + + await broker.start(); + }); + + it('sends jobs to the broker', async () => { + const inputs = makeBaseParityInputs(); + const controller = new AbortController(); + + jest.spyOn(broker, 'enqueueProvingJob'); + jest.spyOn(prover, 'getBaseParityProof'); + + await expect(facade.getBaseParityProof(inputs, controller.signal, 42)).resolves.toBeDefined(); + + expect(broker.enqueueProvingJob).toHaveBeenCalled(); + expect(prover.getBaseParityProof).toHaveBeenCalledWith(inputs, expect.anything(), 42); + }); + + it('handles multiple calls for the same job', async () => { + const inputs = makeBaseParityInputs(); + const controller = new AbortController(); + const promises: Promise[] = []; + + const resultPromise = promiseWithResolvers(); + jest.spyOn(broker, 'enqueueProvingJob'); + jest.spyOn(prover, 'getBaseParityProof').mockReturnValue(resultPromise.promise); + + // send N identical proof requests + const CALLS = 50; + for (let i = 0; i < CALLS; i++) { + promises.push(facade.getBaseParityProof(inputs, controller.signal, 42)); + } + + await sleep(agentPollInterval); + // the broker should have received all of them + expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(CALLS); + + // but really, it should have only enqueued just one + expect(prover.getBaseParityProof).toHaveBeenCalledTimes(1); + expect(prover.getBaseParityProof).toHaveBeenCalledWith(inputs, expect.anything(), 42); + + // now we have 50 promises all waiting on the same result + // resolve the proof + const result = makePublicInputsAndRecursiveProof( + makeParityPublicInputs(), + makeRecursiveProof(RECURSIVE_PROOF_LENGTH), + VerificationKeyData.makeFakeHonk(), + ); + resultPromise.resolve(result); + + // enqueue another N requests for the same jobs + for (let i = 0; i < CALLS; i++) { + promises.push(facade.getBaseParityProof(inputs, controller.signal, 42)); + } + + await sleep(agentPollInterval); + // the broker will have received the new requests + expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(2 * CALLS); + // but no new jobs where created + expect(prover.getBaseParityProof).toHaveBeenCalledTimes(1); + + // and all 2 * N requests will have been resolved with the same result + for (const promise of promises) { + await expect(promise).resolves.toEqual(result); + } + }); + + it('handles proof errors', async () => { + const inputs = makeBaseParityInputs(); + const controller = new AbortController(); + const promises: Promise[] = []; + + const resultPromise = promiseWithResolvers(); + jest.spyOn(broker, 'enqueueProvingJob'); + jest.spyOn(prover, 'getBaseParityProof').mockReturnValue(resultPromise.promise); + + // send N identical proof requests + const CALLS = 50; + for (let i = 0; i < CALLS; i++) { + // wrap the error in a resolved promises so that we don't have unhandled rejections + promises.push(facade.getBaseParityProof(inputs, controller.signal, 42).catch(err => ({ err }))); + } + + await sleep(agentPollInterval); + // the broker should have received all of them + expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(CALLS); + + // but really, it should have only enqueued just one + expect(prover.getBaseParityProof).toHaveBeenCalledTimes(1); + expect(prover.getBaseParityProof).toHaveBeenCalledWith(inputs, expect.anything(), 42); + + resultPromise.reject(new Error('TEST ERROR')); + + // enqueue another N requests for the same jobs + for (let i = 0; i < CALLS; i++) { + promises.push(facade.getBaseParityProof(inputs, controller.signal, 42).catch(err => ({ err }))); + } + + await sleep(agentPollInterval); + // the broker will have received the new requests + expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(2 * CALLS); + // but no new jobs where created + expect(prover.getBaseParityProof).toHaveBeenCalledTimes(1); + + // and all 2 * N requests will have been resolved with the same result + for (const promise of promises) { + await expect(promise).resolves.toEqual({ err: new Error('TEST ERROR') }); + } + }); + + it('handles aborts', async () => { + const inputs = makeBaseParityInputs(); + const controller = new AbortController(); + + const resultPromise = promiseWithResolvers(); + jest.spyOn(broker, 'enqueueProvingJob'); + jest.spyOn(prover, 'getBaseParityProof').mockReturnValue(resultPromise.promise); + + const promise = facade.getBaseParityProof(inputs, controller.signal, 42).catch(err => ({ err })); + + await sleep(agentPollInterval); + expect(prover.getBaseParityProof).toHaveBeenCalled(); + + controller.abort(); + + await expect(promise).resolves.toEqual({ err: new Error('Aborted') }); + }); +}); diff --git a/yarn-project/prover-client/src/proving_broker/caching_broker_facade.ts b/yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts similarity index 72% rename from yarn-project/prover-client/src/proving_broker/caching_broker_facade.ts rename to yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts index bbd154a7e99..dc20621706d 100644 --- a/yarn-project/prover-client/src/proving_broker/caching_broker_facade.ts +++ b/yarn-project/prover-client/src/proving_broker/broker_prover_facade.ts @@ -1,6 +1,5 @@ import { type ProofAndVerificationKey, - type ProverCache, type ProvingJobId, type ProvingJobInputsMap, type ProvingJobProducer, @@ -35,24 +34,20 @@ import { import { sha256 } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { retryUntil } from '@aztec/foundation/retry'; +import { truncate } from '@aztec/foundation/string'; import { InlineProofStore, type ProofStore } from './proof_store.js'; -import { InMemoryProverCache } from './prover_cache/memory.js'; // 20 minutes, roughly the length of an Aztec epoch. If a proof isn't ready in this amount of time then we've failed to prove the whole epoch const MAX_WAIT_MS = 1_200_000; -/** - * A facade around a job broker that generates stable job ids and caches results - */ -export class CachingBrokerFacade implements ServerCircuitProver { +export class BrokerCircuitProverFacade implements ServerCircuitProver { constructor( private broker: ProvingJobProducer, - private cache: ProverCache = new InMemoryProverCache(), private proofStore: ProofStore = new InlineProofStore(), private waitTimeoutMs = MAX_WAIT_MS, private pollIntervalMs = 1000, - private log = createLogger('prover-client:caching-prover-broker'), + private log = createLogger('prover-client:broker-circuit-prover-facade'), ) {} private async enqueueAndWaitForJob( @@ -62,53 +57,23 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber = 0, signal?: AbortSignal, ): Promise { - // first try the cache - let jobEnqueued = false; - let jobRejected = undefined; - try { - const cachedResult = await this.cache.getProvingJobStatus(id); - if (cachedResult.status !== 'not-found') { - this.log.debug(`Found cached result for job=${id}: status=${cachedResult.status}`); - } - - if (cachedResult.status === 'fulfilled') { - const output = await this.proofStore.getProofOutput(cachedResult.value); - if (output.type === type) { - return output.result as ProvingJobResultsMap[T]; - } else { - this.log.warn(`Cached result type mismatch for job=${id}. Expected=${type} but got=${output.type}`); - } - } else if (cachedResult.status === 'rejected') { - jobRejected = cachedResult.reason ?? 'Job rejected for unknown reason'; - } else if (cachedResult.status === 'in-progress' || cachedResult.status === 'in-queue') { - jobEnqueued = true; - } else { - jobEnqueued = false; - } - } catch (err) { - this.log.warn(`Failed to get cached proving job id=${id}: ${err}. Re-running job`); - } - - if (jobRejected) { - throw new Error(jobRejected); - } + const inputsUri = await this.proofStore.saveProofInput(id, type, inputs); + await this.broker.enqueueProvingJob({ + id, + type, + inputsUri, + epochNumber, + }); - if (!jobEnqueued) { - try { - const inputsUri = await this.proofStore.saveProofInput(id, type, inputs); - await this.broker.enqueueProvingJob({ - id, - type, - inputsUri, - epochNumber, - }); - await this.cache.setProvingJobStatus(id, { status: 'in-queue' }); - } catch (err) { - this.log.error(`Failed to enqueue proving job id=${id}: ${err}`); - await this.cache.setProvingJobStatus(id, { status: 'not-found' }); - throw err; - } - } + this.log.verbose( + `Sent proving job to broker id=${id} type=${ProvingRequestType[type]} epochNumber=${epochNumber}`, + { + provingJobId: id, + provingJobType: ProvingRequestType[type], + epochNumber, + inputsUri: truncate(inputsUri), + }, + ); // notify broker of cancelled job const abortFn = async () => { @@ -135,12 +100,6 @@ export class CachingBrokerFacade implements ServerCircuitProver { this.pollIntervalMs / 1000, ); - try { - await this.cache.setProvingJobStatus(id, result); - } catch (err) { - this.log.warn(`Failed to cache proving job id=${id} resultStatus=${result.status}: ${err}`); - } - if (result.status === 'fulfilled') { const output = await this.proofStore.getProofOutput(result.value); if (output.type === type) { @@ -153,8 +112,6 @@ export class CachingBrokerFacade implements ServerCircuitProver { } } finally { signal?.removeEventListener('abort', abortFn); - // we've saved the result in our cache. We can tell the broker to clear its state - await this.broker.cleanUpProvingJobState(id); } } @@ -164,7 +121,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.PUBLIC_VM, inputs), + this.generateId(ProvingRequestType.PUBLIC_VM, inputs, epochNumber), ProvingRequestType.PUBLIC_VM, inputs, epochNumber, @@ -178,7 +135,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.BASE_PARITY, inputs), + this.generateId(ProvingRequestType.BASE_PARITY, inputs, epochNumber), ProvingRequestType.BASE_PARITY, inputs, epochNumber, @@ -192,7 +149,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.BLOCK_MERGE_ROLLUP, input), + this.generateId(ProvingRequestType.BLOCK_MERGE_ROLLUP, input, epochNumber), ProvingRequestType.BLOCK_MERGE_ROLLUP, input, epochNumber, @@ -206,7 +163,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.BLOCK_ROOT_ROLLUP, input), + this.generateId(ProvingRequestType.BLOCK_ROOT_ROLLUP, input, epochNumber), ProvingRequestType.BLOCK_ROOT_ROLLUP, input, epochNumber, @@ -220,7 +177,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input), + this.generateId(ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input, epochNumber), ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP, input, epochNumber, @@ -234,7 +191,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.PRIVATE_KERNEL_EMPTY, inputs), + this.generateId(ProvingRequestType.PRIVATE_KERNEL_EMPTY, inputs, epochNumber), ProvingRequestType.PRIVATE_KERNEL_EMPTY, inputs, epochNumber, @@ -248,7 +205,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.MERGE_ROLLUP, input), + this.generateId(ProvingRequestType.MERGE_ROLLUP, input, epochNumber), ProvingRequestType.MERGE_ROLLUP, input, epochNumber, @@ -261,7 +218,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.PRIVATE_BASE_ROLLUP, baseRollupInput), + this.generateId(ProvingRequestType.PRIVATE_BASE_ROLLUP, baseRollupInput, epochNumber), ProvingRequestType.PRIVATE_BASE_ROLLUP, baseRollupInput, epochNumber, @@ -275,7 +232,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.PUBLIC_BASE_ROLLUP, inputs), + this.generateId(ProvingRequestType.PUBLIC_BASE_ROLLUP, inputs, epochNumber), ProvingRequestType.PUBLIC_BASE_ROLLUP, inputs, epochNumber, @@ -289,7 +246,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.ROOT_PARITY, inputs), + this.generateId(ProvingRequestType.ROOT_PARITY, inputs, epochNumber), ProvingRequestType.ROOT_PARITY, inputs, epochNumber, @@ -303,7 +260,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.ROOT_ROLLUP, input), + this.generateId(ProvingRequestType.ROOT_ROLLUP, input, epochNumber), ProvingRequestType.ROOT_ROLLUP, input, epochNumber, @@ -317,7 +274,7 @@ export class CachingBrokerFacade implements ServerCircuitProver { epochNumber?: number, ): Promise> { return this.enqueueAndWaitForJob( - this.generateId(ProvingRequestType.TUBE_PROOF, tubeInput), + this.generateId(ProvingRequestType.TUBE_PROOF, tubeInput, epochNumber), ProvingRequestType.TUBE_PROOF, tubeInput, epochNumber, @@ -325,8 +282,8 @@ export class CachingBrokerFacade implements ServerCircuitProver { ); } - private generateId(type: ProvingRequestType, inputs: { toBuffer(): Buffer }) { + private generateId(type: ProvingRequestType, inputs: { toBuffer(): Buffer }, epochNumber = 0) { const inputsHash = sha256(inputs.toBuffer()); - return `${ProvingRequestType[type]}:${inputsHash.toString('hex')}`; + return `${epochNumber}:${ProvingRequestType[type]}:${inputsHash.toString('hex')}`; } } diff --git a/yarn-project/prover-client/src/proving_broker/caching_broker_facade.test.ts b/yarn-project/prover-client/src/proving_broker/caching_broker_facade.test.ts deleted file mode 100644 index a72918ade09..00000000000 --- a/yarn-project/prover-client/src/proving_broker/caching_broker_facade.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { type ProvingJobProducer, ProvingRequestType, makePublicInputsAndRecursiveProof } from '@aztec/circuit-types'; -import { RECURSIVE_PROOF_LENGTH, VerificationKeyData, makeRecursiveProof } from '@aztec/circuits.js'; -import { makeBaseParityInputs, makeParityPublicInputs } from '@aztec/circuits.js/testing'; -import { promiseWithResolvers } from '@aztec/foundation/promise'; - -import { jest } from '@jest/globals'; -import { type MockProxy, mock } from 'jest-mock-extended'; - -import { CachingBrokerFacade } from './caching_broker_facade.js'; -import { InlineProofStore } from './proof_store.js'; -import { InMemoryProverCache } from './prover_cache/memory.js'; - -describe('CachingBrokerFacade', () => { - let facade: CachingBrokerFacade; - let cache: InMemoryProverCache; - let proofStore: InlineProofStore; - let broker: MockProxy; - - beforeAll(() => { - jest.useFakeTimers(); - }); - - beforeEach(() => { - broker = mock({ - enqueueProvingJob: jest.fn(), - getProvingJobStatus: jest.fn(), - cancelProvingJob: jest.fn(), - cleanUpProvingJobState: jest.fn(), - waitForJobToSettle: jest.fn(), - }); - cache = new InMemoryProverCache(); - proofStore = new InlineProofStore(); - facade = new CachingBrokerFacade(broker, cache, proofStore); - }); - - it('marks job as in progress', async () => { - const controller = new AbortController(); - void facade.getBaseParityProof(makeBaseParityInputs(), controller.signal); - - await jest.advanceTimersToNextTimerAsync(); - - expect(broker.enqueueProvingJob).toHaveBeenCalled(); - const job = broker.enqueueProvingJob.mock.calls[0][0]; - - await expect(cache.getProvingJobStatus(job.id)).resolves.toEqual({ status: 'in-queue' }); - controller.abort(); - }); - - it('removes the cached value if a job fails to enqueue', async () => { - const { promise, reject } = promiseWithResolvers(); - broker.enqueueProvingJob.mockResolvedValue(promise); - - void facade.getBaseParityProof(makeBaseParityInputs()).catch(() => {}); - await jest.advanceTimersToNextTimerAsync(); - - const job = broker.enqueueProvingJob.mock.calls[0][0]; - - reject(new Error('Failed to enqueue job')); - - await jest.advanceTimersToNextTimerAsync(); - await expect(cache.getProvingJobStatus(job.id)).resolves.toEqual({ status: 'not-found' }); - }); - - it('awaits existing job if in progress', async () => { - const inputs = makeBaseParityInputs(); - void facade.getBaseParityProof(inputs).catch(() => {}); - await jest.advanceTimersToNextTimerAsync(); - expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(1); - - void facade.getBaseParityProof(inputs).catch(() => {}); - await jest.advanceTimersToNextTimerAsync(); - expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(1); - }); - - it('reuses already cached results', async () => { - const { promise, resolve } = promiseWithResolvers(); - broker.enqueueProvingJob.mockResolvedValue(Promise.resolve()); - broker.waitForJobToSettle.mockResolvedValue(promise); - - const inputs = makeBaseParityInputs(); - void facade.getBaseParityProof(inputs); - await jest.advanceTimersToNextTimerAsync(); - - expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(1); - const job = broker.enqueueProvingJob.mock.calls[0][0]; - - const result = makePublicInputsAndRecursiveProof( - makeParityPublicInputs(), - makeRecursiveProof(RECURSIVE_PROOF_LENGTH), - VerificationKeyData.makeFakeHonk(), - ); - - const outputUri = await proofStore.saveProofOutput(job.id, ProvingRequestType.BASE_PARITY, result); - resolve({ - status: 'fulfilled', - value: outputUri, - }); - - await jest.advanceTimersToNextTimerAsync(); - await expect(cache.getProvingJobStatus(job.id)).resolves.toEqual({ status: 'fulfilled', value: outputUri }); - - await expect(facade.getBaseParityProof(inputs)).resolves.toEqual(result); - expect(broker.enqueueProvingJob).toHaveBeenCalledTimes(1); // job was only ever enqueued once - }); - - it('clears broker state after a job resolves', async () => { - const { promise, resolve } = promiseWithResolvers(); - broker.enqueueProvingJob.mockResolvedValue(Promise.resolve()); - broker.waitForJobToSettle.mockResolvedValue(promise); - - const inputs = makeBaseParityInputs(); - void facade.getBaseParityProof(inputs); - await jest.advanceTimersToNextTimerAsync(); - - const job = broker.enqueueProvingJob.mock.calls[0][0]; - const result = makePublicInputsAndRecursiveProof( - makeParityPublicInputs(), - makeRecursiveProof(RECURSIVE_PROOF_LENGTH), - VerificationKeyData.makeFakeHonk(), - ); - const outputUri = await proofStore.saveProofOutput(job.id, ProvingRequestType.BASE_PARITY, result); - resolve({ - status: 'fulfilled', - value: outputUri, - }); - - await jest.advanceTimersToNextTimerAsync(); - expect(broker.cleanUpProvingJobState).toHaveBeenCalled(); - }); - - it('clears broker state after a job is canceled', async () => { - const { promise, resolve } = promiseWithResolvers(); - const catchSpy = jest.fn(); - broker.enqueueProvingJob.mockResolvedValue(Promise.resolve()); - broker.waitForJobToSettle.mockResolvedValue(promise); - - const inputs = makeBaseParityInputs(); - const controller = new AbortController(); - void facade.getBaseParityProof(inputs, controller.signal).catch(catchSpy); - await jest.advanceTimersToNextTimerAsync(); - - expect(broker.cancelProvingJob).not.toHaveBeenCalled(); - controller.abort(); - await jest.advanceTimersToNextTimerAsync(); - expect(broker.cancelProvingJob).toHaveBeenCalled(); - - resolve({ - status: 'rejected', - reason: 'Aborted', - }); - - await jest.advanceTimersToNextTimerAsync(); - expect(broker.cleanUpProvingJobState).toHaveBeenCalled(); - expect(catchSpy).toHaveBeenCalledWith(new Error('Aborted')); - }); -}); diff --git a/yarn-project/prover-client/src/proving_broker/prover_cache/memory.ts b/yarn-project/prover-client/src/proving_broker/prover_cache/memory.ts deleted file mode 100644 index b4da076cbcb..00000000000 --- a/yarn-project/prover-client/src/proving_broker/prover_cache/memory.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ProverCache, ProvingJobStatus } from '@aztec/circuit-types'; - -export class InMemoryProverCache implements ProverCache { - private proofs: Record = {}; - - constructor() {} - - setProvingJobStatus(jobId: string, status: ProvingJobStatus): Promise { - this.proofs[jobId] = status; - return Promise.resolve(); - } - - getProvingJobStatus(jobId: string): Promise { - return Promise.resolve(this.proofs[jobId] ?? { status: 'not-found' }); - } - - close(): Promise { - return Promise.resolve(); - } -} diff --git a/yarn-project/prover-client/src/proving_broker/proving_agent.ts b/yarn-project/prover-client/src/proving_broker/proving_agent.ts index 4d6ad75165a..3c099c8f1a5 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_agent.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_agent.ts @@ -10,6 +10,7 @@ import { } from '@aztec/circuit-types'; import { createLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; +import { truncate } from '@aztec/foundation/string'; import { Timer } from '@aztec/foundation/timer'; import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client'; @@ -107,6 +108,7 @@ export class ProvingAgent implements Traceable { this.currentJobController = new ProvingJobController( job.id, inputs, + job.epochNumber, time, this.circuitProver, this.handleJobResult, @@ -114,13 +116,13 @@ export class ProvingAgent implements Traceable { if (abortedProofJobId) { this.log.info( - `Aborting job id=${abortedProofJobId} type=${abortedProofName} to start new job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncateString( + `Aborting job id=${abortedProofJobId} type=${abortedProofName} to start new job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncate( job.inputsUri, )}`, ); } else { this.log.info( - `Starting job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncateString( + `Starting job id=${this.currentJobController.getJobId()} type=${this.currentJobController.getProofTypeName()} inputsUri=${truncate( job.inputsUri, )}`, ); @@ -147,14 +149,8 @@ export class ProvingAgent implements Traceable { return this.broker.reportProvingJobError(jobId, err.message, retry); } else if (result) { const outputUri = await this.proofStore.saveProofOutput(jobId, type, result); - this.log.info( - `Job id=${jobId} type=${ProvingRequestType[type]} completed outputUri=${truncateString(outputUri)}`, - ); + this.log.info(`Job id=${jobId} type=${ProvingRequestType[type]} completed outputUri=${truncate(outputUri)}`); return this.broker.reportProvingJobSuccess(jobId, outputUri); } }; } - -function truncateString(str: string, length: number = 64): string { - return str.length > length ? str.slice(0, length) + '...' : str; -} diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker.test.ts b/yarn-project/prover-client/src/proving_broker/proving_broker.test.ts index 454e840543f..4782cd61489 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker.test.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker.test.ts @@ -1,4 +1,10 @@ -import { type ProofUri, type ProvingJob, type ProvingJobId, ProvingRequestType } from '@aztec/circuit-types'; +import { + type ProofUri, + type ProvingJob, + type ProvingJobId, + type ProvingJobStatus, + ProvingRequestType, +} from '@aztec/circuit-types'; import { randomBytes } from '@aztec/foundation/crypto'; import { sleep } from '@aztec/foundation/sleep'; import { openTmpStore } from '@aztec/kv-store/lmdb'; @@ -57,6 +63,28 @@ describe.each([ await broker.stop(); }); + it('refuses stale jobs', async () => { + const id = makeProvingJobId(); + await broker.enqueueProvingJob({ + id, + epochNumber: 42, + type: ProvingRequestType.BASE_PARITY, + inputsUri: makeInputsUri(), + }); + expect(await broker.getProvingJobStatus(id)).toEqual({ status: 'in-queue' }); + + const id2 = makeProvingJobId(); + await expect( + broker.enqueueProvingJob({ + id: id2, + epochNumber: 1, + type: ProvingRequestType.PRIVATE_BASE_ROLLUP, + inputsUri: makeInputsUri(), + }), + ).rejects.toThrow(); + await assertJobStatus(id2, 'not-found'); + }); + it('enqueues jobs', async () => { const id = makeProvingJobId(); await broker.enqueueProvingJob({ @@ -204,15 +232,7 @@ describe.each([ inputsUri: makeInputsUri(), }; - const provingJob3: ProvingJob = { - id: makeProvingJobId(), - type: ProvingRequestType.BASE_PARITY, - epochNumber: 3, - inputsUri: makeInputsUri(), - }; - await broker.enqueueProvingJob(provingJob2); - await broker.enqueueProvingJob(provingJob3); await broker.enqueueProvingJob(provingJob1); await getAndAssertNextJobId(provingJob1.id, ProvingRequestType.BASE_PARITY); @@ -639,10 +659,8 @@ describe.each([ await assertJobStatus(id, 'in-progress'); // advance time so job times out because of no heartbeats - await sleep(jobTimeoutMs + brokerIntervalMs); - - // should be back in the queue now - await assertJobStatus(id, 'in-queue'); + await sleep(jobTimeoutMs); + await assertJobTransition(id, 'in-progress', 'in-queue'); }); it('cancel stale jobs that time out', async () => { @@ -659,10 +677,10 @@ describe.each([ await assertJobStatus(id, 'in-progress'); // advance time so job times out because of no heartbeats - await sleep(jobTimeoutMs + brokerIntervalMs); + await sleep(jobTimeoutMs); // should be back in the queue now - await assertJobStatus(id, 'in-queue'); + await assertJobTransition(id, 'in-progress', 'in-queue'); // another agent picks it up await getAndAssertNextJobId(id); @@ -926,48 +944,6 @@ describe.each([ await getAndAssertNextJobId(id2); }); - it('clears job state when job is removed', async () => { - const id1 = makeProvingJobId(); - - await database.addProvingJob({ - id: id1, - type: ProvingRequestType.BASE_PARITY, - epochNumber: 1, - inputsUri: makeInputsUri(), - }); - await database.setProvingJobResult(id1, makeOutputsUri()); - - const id2 = makeProvingJobId(); - await database.addProvingJob({ - id: id2, - type: ProvingRequestType.PRIVATE_BASE_ROLLUP, - epochNumber: 2, - inputsUri: makeInputsUri(), - }); - - await broker.start(); - - await assertJobStatus(id1, 'fulfilled'); - await assertJobStatus(id2, 'in-queue'); - - jest.spyOn(database, 'deleteProvingJobAndResult'); - - await broker.cleanUpProvingJobState(id1); - await sleep(brokerIntervalMs); - expect(database.deleteProvingJobAndResult).toHaveBeenNthCalledWith(1, id1); - - await broker.cancelProvingJob(id2); - await broker.cleanUpProvingJobState(id2); - await sleep(brokerIntervalMs); - - expect(database.deleteProvingJobAndResult).toHaveBeenNthCalledWith(2, id2); - - await expect(broker.getProvingJobStatus(id1)).resolves.toEqual({ status: 'not-found' }); - await expect(broker.getProvingJobStatus(id2)).resolves.toEqual({ status: 'not-found' }); - await assertJobStatus(id1, 'not-found'); - await assertJobStatus(id2, 'not-found'); - }); - it('saves job when enqueued', async () => { await broker.start(); const job: ProvingJob = { @@ -1017,7 +993,7 @@ describe.each([ expect(database.setProvingJobResult).toHaveBeenCalledWith(job.id, expect.any(String)); }); - it('does not retain job result if database fails to save', async () => { + it('saves result even if database fails to save', async () => { await broker.start(); jest.spyOn(database, 'setProvingJobResult').mockRejectedValue(new Error('db error')); const id = makeProvingJobId(); @@ -1028,7 +1004,7 @@ describe.each([ inputsUri: makeInputsUri(), }); await expect(broker.reportProvingJobSuccess(id, makeOutputsUri())).rejects.toThrow(new Error('db error')); - await assertJobStatus(id, 'in-queue'); + await assertJobStatus(id, 'fulfilled'); }); it('saves job error', async () => { @@ -1050,7 +1026,7 @@ describe.each([ expect(database.setProvingJobError).toHaveBeenCalledWith(id, error); }); - it('does not retain job error if database fails to save', async () => { + it('saves job error even if database fails to save', async () => { await broker.start(); jest.spyOn(database, 'setProvingJobError').mockRejectedValue(new Error('db error')); const id = makeProvingJobId(); @@ -1061,7 +1037,7 @@ describe.each([ inputsUri: makeInputsUri(), }); await expect(broker.reportProvingJobError(id, 'test error')).rejects.toThrow(new Error('db error')); - await assertJobStatus(id, 'in-queue'); + await assertJobStatus(id, 'rejected'); }); it('does not save job result if job is unknown', async () => { @@ -1167,10 +1143,30 @@ describe.each([ }); }); - async function assertJobStatus(id: ProvingJobId, status: string) { + async function assertJobStatus(id: ProvingJobId, status: ProvingJobStatus['status']) { await expect(broker.getProvingJobStatus(id)).resolves.toEqual(expect.objectContaining({ status })); } + async function assertJobTransition( + id: ProvingJobId, + currentStatus: ProvingJobStatus['status'], + expectedStatus: ProvingJobStatus['status'], + timeoutMs = 5000, + interval = brokerIntervalMs / 4, + ): Promise { + let status; + const timeout = now() + timeoutMs; + while (now() < timeout) { + ({ status } = await broker.getProvingJobStatus(id)); + if (status !== currentStatus) { + break; + } + await sleep(interval); + } + + expect(status).toEqual(expectedStatus); + } + async function getAndAssertNextJobId(id: ProvingJobId, ...allowList: ProvingRequestType[]) { await expect(broker.getProvingJob({ allowList })).resolves.toEqual( expect.objectContaining({ job: expect.objectContaining({ id }) }), diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker.ts b/yarn-project/prover-client/src/proving_broker/proving_broker.ts index b23b05a5573..7ffbb5244ed 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker.ts @@ -143,7 +143,10 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr public start(): Promise { for (const [item, result] of this.database.allProvingJobs()) { - this.logger.info(`Restoring proving job id=${item.id} settled=${!!result}`); + this.logger.info(`Restoring proving job id=${item.id} settled=${!!result}`, { + provingJobId: item.id, + status: result ? result.status : 'pending', + }); this.jobsCache.set(item.id, item); this.promises.set(item.id, promiseWithResolvers()); @@ -152,7 +155,6 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr this.promises.get(item.id)!.resolve(result); this.resultsCache.set(item.id, result); } else { - this.logger.debug(`Re-enqueuing proving job id=${item.id}`); this.enqueueJobInternal(item); } } @@ -173,37 +175,66 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr if (this.jobsCache.has(job.id)) { const existing = this.jobsCache.get(job.id); assert.deepStrictEqual(job, existing, 'Duplicate proving job ID'); + this.logger.debug(`Duplicate proving job id=${job.id} epochNumber=${job.epochNumber}. Ignoring`, { + provingJobId: job.id, + }); return; } - await this.database.addProvingJob(job); - this.jobsCache.set(job.id, job); - this.enqueueJobInternal(job); + if (this.isJobStale(job)) { + this.logger.warn(`Tried enqueueing stale proving job id=${job.id} epochNumber=${job.epochNumber}`, { + provingJobId: job.id, + }); + throw new Error(`Epoch too old: job epoch ${job.epochNumber}, current epoch: ${this.epochHeight}`); + } + + this.logger.info(`New proving job id=${job.id} epochNumber=${job.epochNumber}`, { provingJobId: job.id }); + try { + // do this first so it acts as a "lock". If this job is enqueued again while we're saving it the if at the top will catch it. + this.jobsCache.set(job.id, job); + await this.database.addProvingJob(job); + this.enqueueJobInternal(job); + } catch (err) { + this.logger.error(`Failed to save proving job id=${job.id}: ${err}`, err, { provingJobId: job.id }); + this.jobsCache.delete(job.id); + throw err; + } } public waitForJobToSettle(id: ProvingJobId): Promise { const promiseWithResolvers = this.promises.get(id); if (!promiseWithResolvers) { + this.logger.warn(`Job id=${id} not found`, { provingJobId: id }); return Promise.resolve({ status: 'rejected', reason: `Job ${id} not found` }); } return promiseWithResolvers.promise; } public async cancelProvingJob(id: ProvingJobId): Promise { + if (!this.jobsCache.has(id)) { + this.logger.warn(`Can't cancel a job that doesn't exist id=${id}`, { provingJobId: id }); + return; + } + // notify listeners of the cancellation if (!this.resultsCache.has(id)) { - this.logger.info(`Cancelling job id=${id}`); + this.logger.info(`Cancelling job id=${id}`, { provingJobId: id }); await this.reportProvingJobError(id, 'Aborted', false); } } - public async cleanUpProvingJobState(id: ProvingJobId): Promise { + private async cleanUpProvingJobState(id: ProvingJobId): Promise { + if (!this.jobsCache.has(id)) { + this.logger.warn(`Can't clean up a job that doesn't exist id=${id}`, { provingJobId: id }); + return; + } + if (!this.resultsCache.has(id)) { - this.logger.warn(`Can't cleanup busy proving job: id=${id}`); + this.logger.warn(`Can't cleanup busy proving job: id=${id}`, { provingJobId: id }); return; } - this.logger.debug(`Cleaning up state for job id=${id}`); + this.logger.debug(`Cleaning up state for job id=${id}`, { provingJobId: id }); await this.database.deleteProvingJobAndResult(id); this.jobsCache.delete(id); this.promises.delete(id); @@ -221,7 +252,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr const item = this.jobsCache.get(id); if (!item) { - this.logger.warn(`Proving job id=${id} not found`); + this.logger.warn(`Proving job id=${id} not found`, { provingJobId: id }); return Promise.resolve({ status: 'not-found' }); } @@ -274,45 +305,68 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr const retries = this.retries.get(id) ?? 0; if (!item) { - this.logger.warn(`Proving job id=${id} not found`); + this.logger.warn(`Can't set error on unknown proving job id=${id} err=${err}`, { provingJoId: id }); return; } if (!info) { - this.logger.warn(`Proving job id=${id} type=${ProvingRequestType[item.type]} not in the in-progress set`); + this.logger.warn(`Proving job id=${id} type=${ProvingRequestType[item.type]} not in the in-progress set`, { + provingJobId: id, + }); } else { this.inProgress.delete(id); } if (this.resultsCache.has(id)) { - this.logger.warn(`Proving job id=${id} already is already settled, ignoring error`); + this.logger.warn(`Proving job id=${id} is already settled, ignoring err=${err}`, { + provingJobId: id, + }); return; } if (retry && retries + 1 < this.maxRetries && !this.isJobStale(item)) { - this.logger.info(`Retrying proving job id=${id} type=${ProvingRequestType[item.type]} retry=${retries + 1}`); + this.logger.info( + `Retrying proving job id=${id} type=${ProvingRequestType[item.type]} retry=${retries + 1} err=${err}`, + { + provingJobId: id, + }, + ); this.retries.set(id, retries + 1); this.enqueueJobInternal(item); this.instrumentation.incRetriedJobs(item.type); return; } - this.logger.warn( + this.logger.info( `Marking proving job as failed id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${ retries + 1 } err=${err}`, + { + provingJobId: id, + }, ); - await this.database.setProvingJobError(id, err); - + // save the result to the cache and notify clients of the job status + // this should work even if our database breaks because the result is cached in memory const result: ProvingJobSettledResult = { status: 'rejected', reason: String(err) }; this.resultsCache.set(id, result); this.promises.get(id)!.resolve(result); + this.instrumentation.incRejectedJobs(item.type); if (info) { const duration = this.msTimeSource() - info.startedAt; this.instrumentation.recordJobDuration(item.type, duration); } + + try { + await this.database.setProvingJobError(id, err); + } catch (saveErr) { + this.logger.error(`Failed to save proving job error status id=${id} jobErr=${err}`, saveErr, { + provingJobId: id, + }); + + throw saveErr; + } } reportProvingJobProgress( @@ -322,12 +376,12 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr ): Promise<{ job: ProvingJob; time: number } | undefined> { const job = this.jobsCache.get(id); if (!job) { - this.logger.warn(`Proving job id=${id} does not exist`); + this.logger.warn(`Proving job id=${id} does not exist`, { provingJobId: id }); return filter ? this.getProvingJob(filter) : Promise.resolve(undefined); } if (this.resultsCache.has(id)) { - this.logger.warn(`Proving job id=${id} has already been completed`); + this.logger.warn(`Proving job id=${id} has already been completed`, { provingJobId: id }); return filter ? this.getProvingJob(filter) : Promise.resolve(undefined); } @@ -336,6 +390,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr if (!metadata) { this.logger.warn( `Proving job id=${id} type=${ProvingRequestType[job.type]} not found in the in-progress cache, adding it`, + { provingJobId: id }, ); // the queue will still contain the item at this point! // we need to be careful when popping off the queue to make sure we're not sending @@ -348,11 +403,12 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr return Promise.resolve(undefined); } else if (startedAt <= metadata.startedAt) { if (startedAt < metadata.startedAt) { - this.logger.debug( + this.logger.info( `Proving job id=${id} type=${ProvingRequestType[job.type]} startedAt=${startedAt} older agent has taken job`, + { provingJobId: id }, ); } else { - this.logger.debug(`Proving job id=${id} type=${ProvingRequestType[job.type]} heartbeat`); + this.logger.debug(`Proving job id=${id} type=${ProvingRequestType[job.type]} heartbeat`, { provingJobId: id }); } metadata.startedAt = startedAt; metadata.lastUpdatedAt = now; @@ -362,6 +418,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr `Proving job id=${id} type=${ ProvingRequestType[job.type] } already being worked on by another agent. Sending new one`, + { provingJobId: id }, ); return this.getProvingJob(filter); } else { @@ -374,31 +431,50 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr const item = this.jobsCache.get(id); const retries = this.retries.get(id) ?? 0; if (!item) { - this.logger.warn(`Proving job id=${id} not found`); + this.logger.warn(`Proving job id=${id} not found`, { provingJobId: id }); return; } if (!info) { - this.logger.warn(`Proving job id=${id} type=${ProvingRequestType[item.type]} not in the in-progress set`); + this.logger.warn(`Proving job id=${id} type=${ProvingRequestType[item.type]} not in the in-progress set`, { + provingJobId: id, + }); } else { this.inProgress.delete(id); } if (this.resultsCache.has(id)) { - this.logger.warn(`Proving job id=${id} already settled, ignoring result`); + this.logger.warn(`Proving job id=${id} already settled, ignoring result`, { provingJobId: id }); return; } - this.logger.debug( + this.logger.info( `Proving job complete id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${retries + 1}`, + { provingJobId: id }, ); - await this.database.setProvingJobResult(id, value); - + // save result to our local cache and notify clients + // if save to database fails, that's ok because we have the result in memory + // if the broker crashes and needs the result again, we're covered because we can just recompute it const result: ProvingJobSettledResult = { status: 'fulfilled', value }; this.resultsCache.set(id, result); this.promises.get(id)!.resolve(result); + this.instrumentation.incResolvedJobs(item.type); + if (info) { + const duration = this.msTimeSource() - info.startedAt; + this.instrumentation.recordJobDuration(item.type, duration); + } + + try { + await this.database.setProvingJobResult(id, value); + } catch (saveErr) { + this.logger.error(`Failed to save proving job result id=${id}`, saveErr, { + provingJobId: id, + }); + + throw saveErr; + } } @trackSpan('ProvingBroker.cleanupPass') @@ -419,7 +495,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr } if (jobsToClean.length > 0) { - this.logger.info(`Cleaning up [${jobsToClean.join(',')}]`); + this.logger.info(`Cleaning up jobs=${jobsToClean.length}`); await asyncPool(this.maxParallelCleanUps, jobsToClean, async jobId => { await this.cleanUpProvingJobState(jobId); }); @@ -431,7 +507,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr for (const [id, metadata] of inProgressEntries) { const item = this.jobsCache.get(id); if (!item) { - this.logger.warn(`Proving job id=${id} not found. Removing it from the queue.`); + this.logger.warn(`Proving job id=${id} not found. Removing it from the queue.`, { provingJobId: id }); this.inProgress.delete(id); continue; } @@ -443,7 +519,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr // the job has timed out and it's also old, just cancel and move on await this.cancelProvingJob(item.id); } else { - this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`); + this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`, { provingJobId: id }); this.inProgress.delete(id); this.enqueueJobInternal(item); this.instrumentation.incTimedOutJobs(item.type); @@ -462,7 +538,6 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer, Tr }); this.enqueuedAt.set(job.id, new Timer()); this.epochHeight = Math.max(this.epochHeight, job.epochNumber); - this.logger.debug(`Enqueued new proving job id=${job.id}`); } private isJobStale(job: ProvingJob) { diff --git a/yarn-project/prover-client/src/proving_broker/proving_job_controller.test.ts b/yarn-project/prover-client/src/proving_broker/proving_job_controller.test.ts index 364703b23cf..1c5fc3c0223 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_job_controller.test.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_job_controller.test.ts @@ -23,6 +23,7 @@ describe('ProvingJobController', () => { type: ProvingRequestType.BASE_PARITY, inputs: makeBaseParityInputs(), }, + 42, 0, prover, onComplete, diff --git a/yarn-project/prover-client/src/proving_broker/proving_job_controller.ts b/yarn-project/prover-client/src/proving_broker/proving_job_controller.ts index 2ce47cbe6f7..0b08e0ad69c 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_job_controller.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_job_controller.ts @@ -30,6 +30,7 @@ export class ProvingJobController { constructor( private jobId: ProvingJobId, private inputs: ProvingJobInputs, + private epochNumber: number, private startedAt: number, private circuitProver: ServerCircuitProver, private onComplete: ProvingJobCompletionCallback, @@ -100,51 +101,51 @@ export class ProvingJobController { const signal = this.abortController.signal; switch (type) { case ProvingRequestType.PUBLIC_VM: { - return await this.circuitProver.getAvmProof(inputs, signal); + return await this.circuitProver.getAvmProof(inputs, signal, this.epochNumber); } case ProvingRequestType.PRIVATE_BASE_ROLLUP: { - return await this.circuitProver.getPrivateBaseRollupProof(inputs, signal); + return await this.circuitProver.getPrivateBaseRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.PUBLIC_BASE_ROLLUP: { - return await this.circuitProver.getPublicBaseRollupProof(inputs, signal); + return await this.circuitProver.getPublicBaseRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.MERGE_ROLLUP: { - return await this.circuitProver.getMergeRollupProof(inputs, signal); + return await this.circuitProver.getMergeRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP: { - return await this.circuitProver.getEmptyBlockRootRollupProof(inputs, signal); + return await this.circuitProver.getEmptyBlockRootRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.BLOCK_ROOT_ROLLUP: { - return await this.circuitProver.getBlockRootRollupProof(inputs, signal); + return await this.circuitProver.getBlockRootRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.BLOCK_MERGE_ROLLUP: { - return await this.circuitProver.getBlockMergeRollupProof(inputs, signal); + return await this.circuitProver.getBlockMergeRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.ROOT_ROLLUP: { - return await this.circuitProver.getRootRollupProof(inputs, signal); + return await this.circuitProver.getRootRollupProof(inputs, signal, this.epochNumber); } case ProvingRequestType.BASE_PARITY: { - return await this.circuitProver.getBaseParityProof(inputs, signal); + return await this.circuitProver.getBaseParityProof(inputs, signal, this.epochNumber); } case ProvingRequestType.ROOT_PARITY: { - return await this.circuitProver.getRootParityProof(inputs, signal); + return await this.circuitProver.getRootParityProof(inputs, signal, this.epochNumber); } case ProvingRequestType.PRIVATE_KERNEL_EMPTY: { - return await this.circuitProver.getEmptyPrivateKernelProof(inputs, signal); + return await this.circuitProver.getEmptyPrivateKernelProof(inputs, signal, this.epochNumber); } case ProvingRequestType.TUBE_PROOF: { - return await this.circuitProver.getTubeProof(inputs, signal); + return await this.circuitProver.getTubeProof(inputs, signal, this.epochNumber); } default: { diff --git a/yarn-project/prover-client/src/proving_broker/rpc.ts b/yarn-project/prover-client/src/proving_broker/rpc.ts index 0d63dcebeac..3dec6bedbdb 100644 --- a/yarn-project/prover-client/src/proving_broker/rpc.ts +++ b/yarn-project/prover-client/src/proving_broker/rpc.ts @@ -28,7 +28,6 @@ const GetProvingJobResponse = z.object({ export const ProvingJobProducerSchema: ApiSchemaFor = { enqueueProvingJob: z.function().args(ProvingJob).returns(z.void()), getProvingJobStatus: z.function().args(ProvingJobId).returns(ProvingJobStatus), - cleanUpProvingJobState: z.function().args(ProvingJobId).returns(z.void()), cancelProvingJob: z.function().args(ProvingJobId).returns(z.void()), waitForJobToSettle: z.function().args(ProvingJobId).returns(ProvingJobSettledResult), }; diff --git a/yarn-project/prover-client/src/test/mock_prover.ts b/yarn-project/prover-client/src/test/mock_prover.ts index a4ab77fc24d..baf581d8f8e 100644 --- a/yarn-project/prover-client/src/test/mock_prover.ts +++ b/yarn-project/prover-client/src/test/mock_prover.ts @@ -58,8 +58,12 @@ export class TestBroker implements ProvingJobProducer { agentCount: number, prover: ServerCircuitProver, private proofStore: ProofStore = new InlineProofStore(), + agentPollInterval = 100, ) { - this.agents = times(agentCount, () => new ProvingAgent(this.broker, proofStore, prover, new NoopTelemetryClient())); + this.agents = times( + agentCount, + () => new ProvingAgent(this.broker, proofStore, prover, new NoopTelemetryClient(), undefined, agentPollInterval), + ); } public async start() { @@ -82,9 +86,6 @@ export class TestBroker implements ProvingJobProducer { getProvingJobStatus(id: ProvingJobId): Promise { return this.broker.getProvingJobStatus(id); } - cleanUpProvingJobState(id: ProvingJobId): Promise { - return this.broker.cleanUpProvingJobState(id); - } cancelProvingJob(id: string): Promise { return this.broker.cancelProvingJob(id); } diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 7a4ec700721..c1a5f27026a 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -12,14 +12,12 @@ import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { createWorldStateSynchronizer } from '@aztec/world-state'; -import { join } from 'path'; import { createPublicClient, getAddress, getContract, http } from 'viem'; import { createBondManager } from './bond/factory.js'; import { type ProverNodeConfig, type QuoteProviderConfig } from './config.js'; import { ClaimsMonitor } from './monitors/claims-monitor.js'; import { EpochMonitor } from './monitors/epoch-monitor.js'; -import { ProverCacheManager } from './prover-cache/cache_manager.js'; import { createProverCoordination } from './prover-coordination/factory.js'; import { ProverNode, type ProverNodeOptions } from './prover-node.js'; import { HttpQuoteProvider } from './quote-provider/http.js'; @@ -78,9 +76,6 @@ export async function createProverNode( const walletClient = publisher.getClient(); const bondManager = await createBondManager(rollupContract, walletClient, config); - const cacheDir = config.cacheDir ? join(config.cacheDir, `prover_${config.proverId}`) : undefined; - const cacheManager = new ProverCacheManager(cacheDir); - return new ProverNode( prover, publisher, @@ -95,7 +90,6 @@ export async function createProverNode( epochMonitor, bondManager, telemetry, - cacheManager, proverNodeConfig, ); } diff --git a/yarn-project/prover-node/src/prover-cache/cache_manager.ts b/yarn-project/prover-node/src/prover-cache/cache_manager.ts deleted file mode 100644 index 497300d1e42..00000000000 --- a/yarn-project/prover-node/src/prover-cache/cache_manager.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { type ProverCache } from '@aztec/circuit-types'; -import { createLogger } from '@aztec/foundation/log'; -import { AztecLmdbStore } from '@aztec/kv-store/lmdb'; -import { InMemoryProverCache } from '@aztec/prover-client'; - -import { type Dirent } from 'fs'; -import { mkdir, readFile, readdir, rm, writeFile } from 'fs/promises'; -import { join } from 'path'; - -import { KVProverCache } from './kv_cache.js'; - -const EPOCH_DIR_PREFIX = 'epoch'; -const EPOCH_DIR_SEPARATOR = '_'; -const EPOCH_HASH_FILENAME = 'epoch_hash.txt'; - -export class ProverCacheManager { - constructor(private cacheDir?: string, private log = createLogger('prover-node:cache-manager')) {} - - public async openCache(epochNumber: bigint, epochHash: Buffer): Promise { - if (!this.cacheDir) { - return new InMemoryProverCache(); - } - - const epochDir = EPOCH_DIR_PREFIX + EPOCH_DIR_SEPARATOR + epochNumber; - const dataDir = join(this.cacheDir, epochDir); - - const storedEpochHash = await readFile(join(dataDir, EPOCH_HASH_FILENAME), 'hex').catch(() => Buffer.alloc(0)); - if (storedEpochHash.toString() !== epochHash.toString()) { - await rm(dataDir, { recursive: true, force: true }); - } - - await mkdir(dataDir, { recursive: true }); - await writeFile(join(dataDir, EPOCH_HASH_FILENAME), epochHash.toString('hex')); - - const store = AztecLmdbStore.open(dataDir); - this.log.debug(`Created new database for epoch ${epochNumber} at ${dataDir}`); - const cleanup = () => store.close(); - return new KVProverCache(store, cleanup); - } - - /** - * Removes all caches for epochs older than the given epoch (including) - * @param upToAndIncludingEpoch - The epoch number up to which to remove caches - */ - public async removeStaleCaches(upToAndIncludingEpoch: bigint): Promise { - if (!this.cacheDir) { - return; - } - - const entries: Dirent[] = await readdir(this.cacheDir, { withFileTypes: true }).catch(() => []); - - for (const item of entries) { - if (!item.isDirectory()) { - continue; - } - - const [prefix, epochNumber] = item.name.split(EPOCH_DIR_SEPARATOR); - if (prefix !== EPOCH_DIR_PREFIX) { - continue; - } - - const epochNumberInt = BigInt(epochNumber); - if (epochNumberInt <= upToAndIncludingEpoch) { - this.log.info(`Removing old epoch database for epoch ${epochNumberInt} at ${join(this.cacheDir, item.name)}`); - await rm(join(this.cacheDir, item.name), { recursive: true }); - } - } - } -} diff --git a/yarn-project/prover-node/src/prover-cache/kv_cache.ts b/yarn-project/prover-node/src/prover-cache/kv_cache.ts deleted file mode 100644 index 82b216e384a..00000000000 --- a/yarn-project/prover-node/src/prover-cache/kv_cache.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ProverCache, ProvingJobStatus } from '@aztec/circuit-types'; -import type { AztecKVStore, AztecMap } from '@aztec/kv-store'; - -export class KVProverCache implements ProverCache { - private proofs: AztecMap; - - constructor(store: AztecKVStore, private cleanup?: () => Promise) { - this.proofs = store.openMap('prover_node_proof_status'); - } - - getProvingJobStatus(jobId: string): Promise { - const item = this.proofs.get(jobId); - if (!item) { - return Promise.resolve({ status: 'not-found' }); - } - - return Promise.resolve(JSON.parse(item)); - } - - setProvingJobStatus(jobId: string, status: ProvingJobStatus): Promise { - return this.proofs.set(jobId, JSON.stringify(status)); - } - - async close(): Promise { - await this.cleanup?.(); - } -} diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index c7b70d6136a..931396a2676 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -8,7 +8,6 @@ import { type L2BlockSource, type MerkleTreeWriteOperations, P2PClientType, - type ProverCache, type ProverCoordination, WorldStateRunningState, type WorldStateSynchronizer, @@ -32,7 +31,6 @@ import { type BondManager } from './bond/bond-manager.js'; import { type EpochProvingJob } from './job/epoch-proving-job.js'; import { ClaimsMonitor } from './monitors/claims-monitor.js'; import { EpochMonitor } from './monitors/epoch-monitor.js'; -import { ProverCacheManager } from './prover-cache/cache_manager.js'; import { ProverNode, type ProverNodeOptions } from './prover-node.js'; import { type QuoteProvider } from './quote-provider/index.js'; import { type QuoteSigner } from './quote-signer.js'; @@ -99,7 +97,6 @@ describe('prover-node', () => { epochMonitor, bondManager, telemetryClient, - new ProverCacheManager(), config, ); @@ -385,7 +382,6 @@ describe('prover-node', () => { protected override doCreateEpochProvingJob( epochNumber: bigint, _blocks: L2Block[], - _cache: ProverCache, _publicProcessorFactory: PublicProcessorFactory, cleanUp: (job: EpochProvingJob) => Promise, ): EpochProvingJob { diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index ceabaf00e33..cc7a5b126f5 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -7,7 +7,6 @@ import { type L2Block, type L2BlockSource, type P2PClientType, - type ProverCache, type ProverCoordination, type ProverNodeApi, type Service, @@ -16,7 +15,6 @@ import { } from '@aztec/circuit-types'; import { type ContractDataSource } from '@aztec/circuits.js'; import { compact } from '@aztec/foundation/collection'; -import { sha256 } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { type Maybe } from '@aztec/foundation/types'; import { type P2P } from '@aztec/p2p'; @@ -29,7 +27,6 @@ import { EpochProvingJob, type EpochProvingJobState } from './job/epoch-proving- import { ProverNodeMetrics } from './metrics.js'; import { type ClaimsMonitor, type ClaimsMonitorHandler } from './monitors/claims-monitor.js'; import { type EpochMonitor, type EpochMonitorHandler } from './monitors/epoch-monitor.js'; -import { type ProverCacheManager } from './prover-cache/cache_manager.js'; import { type QuoteProvider } from './quote-provider/index.js'; import { type QuoteSigner } from './quote-signer.js'; @@ -67,7 +64,6 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr private readonly epochsMonitor: EpochMonitor, private readonly bondManager: BondManager, private readonly telemetryClient: TelemetryClient, - private readonly proverCacheManager: ProverCacheManager, options: Partial = {}, ) { this.options = { @@ -266,16 +262,12 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr // Create a processor using the forked world state const publicProcessorFactory = new PublicProcessorFactory(this.contractDataSource, this.telemetryClient); - const epochHash = sha256(Buffer.concat(blocks.map(block => block.hash().toBuffer()))); - const proverCache = await this.proverCacheManager.openCache(epochNumber, epochHash); - - const cleanUp = async () => { - await proverCache.close(); - await this.proverCacheManager.removeStaleCaches(epochNumber); + const cleanUp = () => { this.jobs.delete(job.getId()); + return Promise.resolve(); }; - const job = this.doCreateEpochProvingJob(epochNumber, blocks, proverCache, publicProcessorFactory, cleanUp); + const job = this.doCreateEpochProvingJob(epochNumber, blocks, publicProcessorFactory, cleanUp); this.jobs.set(job.getId(), job); return job; } @@ -284,7 +276,6 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr protected doCreateEpochProvingJob( epochNumber: bigint, blocks: L2Block[], - proverCache: ProverCache, publicProcessorFactory: PublicProcessorFactory, cleanUp: () => Promise, ) { @@ -292,7 +283,7 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr this.worldState, epochNumber, blocks, - this.prover.createEpochProver(proverCache), + this.prover.createEpochProver(), publicProcessorFactory, this.publisher, this.l2BlockSource, From bbbf38b35c7f04414eeb7991a1ee45b19b16664f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Mon, 16 Dec 2024 12:01:22 -0600 Subject: [PATCH 05/10] refactor: `getLogsByTags` request batching in `syncTaggedLogs` (#10716) --- .../src/interfaces/aztec-node.ts | 3 +- .../src/structs/indexed_tagging_secret.ts | 6 +- .../pxe/src/simulator_oracle/index.ts | 201 ++++++++++-------- .../simulator_oracle/simulator_oracle.test.ts | 41 ++-- .../pxe/src/simulator_oracle/tagging_utils.ts | 46 +--- 5 files changed, 149 insertions(+), 148 deletions(-) diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index 427ff8d88ac..e1979488813 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -315,7 +315,8 @@ export interface AztecNode * Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag). * @param tags - The tags to filter the logs by. * @returns For each received tag, an array of matching logs and metadata (e.g. tx hash) is returned. An empty - array implies no logs match that tag. + * array implies no logs match that tag. There can be multiple logs for 1 tag because tag reuse can happen + * --> e.g. when sending a note from multiple unsynched devices. */ getLogsByTags(tags: Fr[]): Promise; diff --git a/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts b/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts index 4a884180eb8..37b98d62282 100644 --- a/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts +++ b/yarn-project/circuits.js/src/structs/indexed_tagging_secret.ts @@ -3,7 +3,11 @@ import { poseidon2Hash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; export class IndexedTaggingSecret { - constructor(public appTaggingSecret: Fr, public index: number) {} + constructor(public appTaggingSecret: Fr, public index: number) { + if (index < 0) { + throw new Error('IndexedTaggingSecret index out of bounds'); + } + } toFields(): Fr[] { return [this.appTaggingSecret, new Fr(this.index)]; diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index fdd3058ab1d..acd71477f52 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -38,7 +38,7 @@ import { type IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; import { produceNoteDaos } from '../note_decryption_utils/produce_note_daos.js'; import { getAcirSimulator } from '../simulator/index.js'; -import { getInitialIndexes, getLeftMostIndexedTaggingSecrets, getRightMostIndexes } from './tagging_utils.js'; +import { getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js'; /** * A data oracle that provides information needed for simulating a transaction. @@ -424,116 +424,137 @@ export class SimulatorOracle implements DBOracle { // Half the size of the window we slide over the tagging secret indexes. const WINDOW_HALF_SIZE = 10; + // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles. + // However it is impossible at the moment due to the language not supporting nested slices. + // This nesting is necessary because for a given set of tags we don't + // know how many logs we will get back. Furthermore, these logs are of undetermined + // length, since we don't really know the note they correspond to until we decrypt them. + const recipients = scopes ? scopes : await this.keyStore.getAccounts(); - // A map of never-before-seen logs going from recipient address to logs - const newLogsMap = new Map(); + // A map of logs going from recipient address to logs. Note that the logs might have been processed before + // due to us having a sliding window that "looks back" for logs as well. (We look back as there is no guarantee + // that a logs will be received ordered by a given tax index and that the tags won't be reused). + const logsMap = new Map(); const contractName = await this.contractDataOracle.getDebugContractName(contractAddress); for (const recipient of recipients) { - const logs: TxScopedL2Log[] = []; - // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles. - // However it is impossible at the moment due to the language not supporting nested slices. - // This nesting is necessary because for a given set of tags we don't - // know how many logs we will get back. Furthermore, these logs are of undetermined - // length, since we don't really know the note they correspond to until we decrypt them. - - // 1. Get all the secrets for the recipient and sender pairs (#9365) - const indexedTaggingSecrets = await this.#getIndexedTaggingSecretsForContacts(contractAddress, recipient); - - // 1.1 Set up a sliding window with an offset. Chances are the sender might have messed up - // and inadvertently incremented their index without us getting any logs (for example, in case - // of a revert). If we stopped looking for logs the first time we don't receive any logs for a tag, - // we might never receive anything from that sender again. - // Also there's a possibility that we have advanced our index, but the sender has reused it, - // so we might have missed some logs. For these reasons, we have to look both back and ahead of - // the stored index. - - // App tagging secrets along with an index in a window to check in the current iteration. Called current because - // this value will be updated as we iterate through the window. - let currentSecrets = getLeftMostIndexedTaggingSecrets(indexedTaggingSecrets, WINDOW_HALF_SIZE); - // Right-most indexes in a window to check stored in a key-value map where key is the app tagging secret - // and value is the index to check (the right-most index in the window). - const rightMostIndexesMap = getRightMostIndexes(indexedTaggingSecrets, WINDOW_HALF_SIZE); + const logsForRecipient: TxScopedL2Log[] = []; + + // Get all the secrets for the recipient and sender pairs (#9365) + const secrets = await this.#getIndexedTaggingSecretsForContacts(contractAddress, recipient); + + // We fetch logs for a window of indexes in a range: + // . + // + // We use this window approach because it could happen that a sender might have messed up and inadvertently + // incremented their index without us getting any logs (for example, in case of a revert). If we stopped looking + // for logs the first time we don't receive any logs for a tag, we might never receive anything from that sender again. + // Also there's a possibility that we have advanced our index, but the sender has reused it, so we might have missed + // some logs. For these reasons, we have to look both back and ahead of the stored index. + let secretsAndWindows = secrets.map(secret => { + return { + appTaggingSecret: secret.appTaggingSecret, + leftMostIndex: Math.max(0, secret.index - WINDOW_HALF_SIZE), + rightMostIndex: secret.index + WINDOW_HALF_SIZE, + }; + }); + + // As we iterate we store the largest index we have seen for a given secret to later on store it in the db. + const newLargestIndexMapToStore: { [k: string]: number } = {}; + // The initial/unmodified indexes of the secrets stored in a key-value map where key is the app tagging secret. - const initialIndexesMap = getInitialIndexes(indexedTaggingSecrets); - // A map of indexes to increment for secrets for which we have found logs with an index higher than the one - // stored. - const indexesToIncrementMap: { [k: string]: number } = {}; - - while (currentSecrets.length > 0) { - // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380) - const currentTags = currentSecrets.map(secret => - // We compute the siloed tags since we need the tags as they appear in the log. + const initialIndexesMap = getInitialIndexesMap(secrets); + + while (secretsAndWindows.length > 0) { + const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows); + const tagsForTheWholeWindow = secretsForTheWholeWindow.map(secret => secret.computeSiloedTag(recipient, contractAddress), ); + // We store the new largest indexes we find in the iteration in the following map to later on construct + // a new set of secrets and windows to fetch logs for. + const newLargestIndexMapForIteration: { [k: string]: number } = {}; + // Fetch the logs for the tags and iterate over them - const logsByTags = await this.aztecNode.getLogsByTags(currentTags); - const secretsWithNewIndex: IndexedTaggingSecret[] = []; + const logsByTags = await this.aztecNode.getLogsByTags(tagsForTheWholeWindow); + logsByTags.forEach((logsByTag, logIndex) => { - const { appTaggingSecret: currentSecret, index: currentIndex } = currentSecrets[logIndex]; - const currentSecretAsStr = currentSecret.toString(); - this.log.debug(`Syncing logs for recipient ${recipient} at contract ${contractName}(${contractAddress})`, { - recipient, - secret: currentSecret, - index: currentIndex, - contractName, - contractAddress, - }); - // 3.1. Append logs to the list and increment the index for the tags that have logs (#9380) if (logsByTag.length > 0) { - const newIndex = currentIndex + 1; - this.log.debug( - `Found ${logsByTag.length} logs as recipient ${recipient}. Incrementing index to ${newIndex} at contract ${contractName}(${contractAddress})`, - { - recipient, - secret: currentSecret, - newIndex, - contractName, - contractAddress, - }, - ); - logs.push(...logsByTag); - - if (currentIndex >= initialIndexesMap[currentSecretAsStr]) { - // 3.2. We found an index higher than the stored/initial one so we update it in the db later on (#9380) - indexesToIncrementMap[currentSecretAsStr] = newIndex; - // 3.3. We found an index higher than the initial one so we slide the window. - rightMostIndexesMap[currentSecretAsStr] = currentIndex + WINDOW_HALF_SIZE; + // The logs for the given tag exist so we store them for later processing + logsForRecipient.push(...logsByTag); + + // We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether + // a new largest index have been found. + const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex]; + const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()]; + + this.log.debug(`Found ${logsByTag.length} logs as recipient ${recipient}`, { + recipient, + secret: secretCorrespondingToLog.appTaggingSecret, + contractName, + contractAddress, + }); + + if ( + secretCorrespondingToLog.index >= initialIndex && + (newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] === undefined || + secretCorrespondingToLog.index >= + newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()]) + ) { + // We have found a new largest index so we store it for later processing (storing it in the db + fetching + // the difference of the window sets of current and the next iteration) + newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] = + secretCorrespondingToLog.index + 1; + + this.log.debug( + `Incrementing index to ${ + secretCorrespondingToLog.index + 1 + } at contract ${contractName}(${contractAddress})`, + ); } } - // 3.4 Keep increasing the index (inside the window) temporarily for the tags that have no logs - // There's a chance the sender missed some and we want to catch up - if (currentIndex < rightMostIndexesMap[currentSecretAsStr]) { - const newTaggingSecret = new IndexedTaggingSecret(currentSecret, currentIndex + 1); - secretsWithNewIndex.push(newTaggingSecret); - } }); - // We store the new indexes for the secrets that have logs with an index higher than the one stored. - await this.db.setTaggingSecretsIndexesAsRecipient( - Object.keys(indexesToIncrementMap).map( - secret => new IndexedTaggingSecret(Fr.fromHexString(secret), indexesToIncrementMap[secret]), - ), - ); + // Now based on the new largest indexes we found, we will construct a new secrets and windows set to fetch logs + // for. Note that it's very unlikely that a new log from the current window would appear between the iterations + // so we fetch the logs only for the difference of the window sets. + const newSecretsAndWindows = []; + for (const [appTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) { + const secret = secrets.find(secret => secret.appTaggingSecret.toString() === appTaggingSecret); + if (secret) { + newSecretsAndWindows.push({ + appTaggingSecret: secret.appTaggingSecret, + // We set the left most index to the new index to avoid fetching the same logs again + leftMostIndex: newIndex, + rightMostIndex: newIndex + WINDOW_HALF_SIZE, + }); + + // We store the new largest index in the map to later store it in the db. + newLargestIndexMapToStore[appTaggingSecret] = newIndex; + } else { + throw new Error( + `Secret not found for appTaggingSecret ${appTaggingSecret}. This is a bug as it should never happen!`, + ); + } + } - // We've processed all the current secret-index pairs so we proceed to the next iteration. - currentSecrets = secretsWithNewIndex; + // Now we set the new secrets and windows and proceed to the next iteration. + secretsAndWindows = newSecretsAndWindows; } - newLogsMap.set( + // We filter the logs by block number and store them in the map. + logsMap.set( recipient.toString(), - // Remove logs with a block number higher than the max block number - // Duplicates are likely to happen due to the sliding window, so we also filter them out - logs.filter( - (log, index, self) => - // The following condition is true if the log has small enough block number and is unique - // --> the right side of the && is true if the index of the current log is the first occurrence - // of the log in the array --> that way we ensure uniqueness. - log.blockNumber <= maxBlockNumber && index === self.findIndex(otherLog => otherLog.equals(log)), + logsForRecipient.filter(log => log.blockNumber <= maxBlockNumber), + ); + + // At this point we have processed all the logs for the recipient so we store the new largest indexes in the db. + await this.db.setTaggingSecretsIndexesAsRecipient( + Object.entries(newLargestIndexMapToStore).map( + ([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index), ), ); } - return newLogsMap; + return logsMap; } /** diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts index b291362943c..722eaf8e944 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -235,7 +235,8 @@ describe('Simulator oracle', () => { const senderOffset = 0; generateMockLogs(senderOffset); const syncedLogs = await simulatorOracle.syncTaggedLogs(contractAddress, 3); - // We expect to have all logs intended for the recipient, one per sender + 1 with a duplicated tag for the first one + half of the logs for the second index + // We expect to have all logs intended for the recipient, one per sender + 1 with a duplicated tag for the first + // one + half of the logs for the second index expect(syncedLogs.get(recipient.address.toString())).toHaveLength(NUM_SENDERS + 1 + NUM_SENDERS / 2); // Recompute the secrets (as recipient) to ensure indexes are updated @@ -254,9 +255,9 @@ describe('Simulator oracle', () => { expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); - // We should have called the node 12 times: - // 2 times with logs (sliding the window) + 10 times with no results (window size) - expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2 + SENDER_OFFSET_WINDOW_SIZE); + // We should have called the node 2 times: + // 2 times: first time during initial request, second time after pushing the edge of the window once + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2); }); it('should sync tagged logs as senders', async () => { @@ -334,9 +335,9 @@ describe('Simulator oracle', () => { expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([6, 6, 6, 6, 6, 7, 7, 7, 7, 7]); - // We should have called the node 17 times: - // 5 times with no results (sender offset) + 2 times with logs (sliding the window) + 10 times with no results (window size) - expect(aztecNode.getLogsByTags.mock.calls.length).toBe(5 + 2 + SENDER_OFFSET_WINDOW_SIZE); + // We should have called the node 2 times: + // 2 times: first time during initial request, second time after pushing the edge of the window once + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2); }); it("should sync tagged logs for which indexes are not updated if they're inside the window", async () => { @@ -360,16 +361,16 @@ describe('Simulator oracle', () => { expect(syncedLogs.get(recipient.address.toString())).toHaveLength(NUM_SENDERS + 1 + NUM_SENDERS / 2); // First sender should have 2 logs, but keep index 2 since they were built using the same tag - // Next 4 senders hould also have index 2 = offset + 1 + // Next 4 senders should also have index 2 = offset + 1 // Last 5 senders should have index 3 = offset + 2 const indexes = await database.getTaggingSecretsIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([2, 2, 2, 2, 2, 3, 3, 3, 3, 3]); - // We should have called the node 13 times: - // 1 time without logs + 2 times with logs (sliding the window) + 10 times with no results (window size) - expect(aztecNode.getLogsByTags.mock.calls.length).toBe(3 + SENDER_OFFSET_WINDOW_SIZE); + // We should have called the node 2 times: + // first time during initial request, second time after pushing the edge of the window once + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2); }); it("should not sync tagged logs for which indexes are not updated if they're outside the window", async () => { @@ -398,9 +399,8 @@ describe('Simulator oracle', () => { expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([11, 11, 11, 11, 11, 11, 11, 11, 11, 11]); - // We should have called the node SENDER_OFFSET_WINDOW_SIZE + 1 (with logs) + SENDER_OFFSET_WINDOW_SIZE: - // Once for index 1 (NUM_SENDERS/2 logs) + 2 times the sliding window (no logs each time) - expect(aztecNode.getLogsByTags.mock.calls.length).toBe(1 + 2 * SENDER_OFFSET_WINDOW_SIZE); + // We should have called the node once and that is only for the first window + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(1); }); it('should sync tagged logs from scratch after a DB wipe', async () => { @@ -422,8 +422,9 @@ describe('Simulator oracle', () => { // No logs should be synced since we start from index 2 = 12 - window_size expect(syncedLogs.get(recipient.address.toString())).toHaveLength(0); - // We should have called the node 21 times (window size + current_index + window size) - expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2 * SENDER_OFFSET_WINDOW_SIZE + 1); + // Since no logs were synced, window edge hash not been pushed and for this reason we should have called + // the node only once for the initial window + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(1); aztecNode.getLogsByTags.mockClear(); @@ -433,16 +434,16 @@ describe('Simulator oracle', () => { syncedLogs = await simulatorOracle.syncTaggedLogs(contractAddress, 3); // First sender should have 2 logs, but keep index 1 since they were built using the same tag - // Next 4 senders hould also have index 1 = offset + 1 + // Next 4 senders should also have index 1 = offset + 1 // Last 5 senders should have index 2 = offset + 2 const indexes = await database.getTaggingSecretsIndexesAsRecipient(secrets); expect(indexes).toHaveLength(NUM_SENDERS); expect(indexes).toEqual([1, 1, 1, 1, 1, 2, 2, 2, 2, 2]); - // We should have called the node 12 times: - // 2 times with logs (sliding the window) + 10 times with no results (window size) - expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2 + SENDER_OFFSET_WINDOW_SIZE); + // We should have called the node 2 times: + // first time during initial request, second time after pushing the edge of the window once + expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2); }); it('should not sync tagged logs with a blockNumber > maxBlockNumber', async () => { diff --git a/yarn-project/pxe/src/simulator_oracle/tagging_utils.ts b/yarn-project/pxe/src/simulator_oracle/tagging_utils.ts index 52ed10a1cfc..82759eaf038 100644 --- a/yarn-project/pxe/src/simulator_oracle/tagging_utils.ts +++ b/yarn-project/pxe/src/simulator_oracle/tagging_utils.ts @@ -1,41 +1,15 @@ -import { IndexedTaggingSecret } from '@aztec/circuits.js'; +import { type Fr, IndexedTaggingSecret } from '@aztec/circuits.js'; -/** - * Gets indexed tagging secrets with leftmost indexes. - * @param indexedTaggingSecrets - The indexed tagging secrets to get the leftmost indexed tagging secrets from. - * @param windowHalfSize- The half size of the window to slide over the tagging secret indexes. - * @returns The leftmost indexed tagging secrets. - */ -export function getLeftMostIndexedTaggingSecrets( - indexedTaggingSecrets: IndexedTaggingSecret[], - windowHalfSize: number, +export function getIndexedTaggingSecretsForTheWindow( + secretsAndWindows: { appTaggingSecret: Fr; leftMostIndex: number; rightMostIndex: number }[], ): IndexedTaggingSecret[] { - return indexedTaggingSecrets.map( - indexedTaggingSecret => - new IndexedTaggingSecret( - indexedTaggingSecret.appTaggingSecret, - Math.max(0, indexedTaggingSecret.index - windowHalfSize), - ), - ); -} - -/** - * Creates a map from app tagging secret to rightmost index. - * @param indexedTaggingSecrets - The indexed tagging secrets to get the rightmost indexes from. - * @param windowHalfSize- The half size of the window to slide over the tagging secret indexes. - * @returns The map from app tagging secret to rightmost index. - */ -export function getRightMostIndexes( - indexedTaggingSecrets: IndexedTaggingSecret[], - windowHalfSize: number, -): { [k: string]: number } { - const rightMostIndexes: { [k: string]: number } = {}; - - for (const indexedTaggingSecret of indexedTaggingSecrets) { - rightMostIndexes[indexedTaggingSecret.appTaggingSecret.toString()] = indexedTaggingSecret.index + windowHalfSize; + const secrets: IndexedTaggingSecret[] = []; + for (const secretAndWindow of secretsAndWindows) { + for (let i = secretAndWindow.leftMostIndex; i <= secretAndWindow.rightMostIndex; i++) { + secrets.push(new IndexedTaggingSecret(secretAndWindow.appTaggingSecret, i)); + } } - - return rightMostIndexes; + return secrets; } /** @@ -43,7 +17,7 @@ export function getRightMostIndexes( * @param indexedTaggingSecrets - The indexed tagging secrets to get the initial indexes from. * @returns The map from app tagging secret to initial index. */ -export function getInitialIndexes(indexedTaggingSecrets: IndexedTaggingSecret[]): { [k: string]: number } { +export function getInitialIndexesMap(indexedTaggingSecrets: IndexedTaggingSecret[]): { [k: string]: number } { const initialIndexes: { [k: string]: number } = {}; for (const indexedTaggingSecret of indexedTaggingSecrets) { From 1e15bf58390f6c15afc3b430edd89b4c28137c2b Mon Sep 17 00:00:00 2001 From: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> Date: Tue, 17 Dec 2024 02:39:16 +0800 Subject: [PATCH 06/10] feat(blobs): add consensus client url to config (#10059) --- yarn-project/archiver/src/archiver/config.ts | 8 ++++++++ yarn-project/foundation/src/config/env_var.ts | 1 + 2 files changed, 9 insertions(+) diff --git a/yarn-project/archiver/src/archiver/config.ts b/yarn-project/archiver/src/archiver/config.ts index d739314d468..32efc706cf6 100644 --- a/yarn-project/archiver/src/archiver/config.ts +++ b/yarn-project/archiver/src/archiver/config.ts @@ -21,6 +21,9 @@ export type ArchiverConfig = { /** URL for an archiver service. If set, will return an archiver client as opposed to starting a new one. */ archiverUrl?: string; + /** URL for an L1 consensus client */ + l1ConsensusClientUrl: string; + /** The polling interval in ms for retrieving new L2 blocks and encrypted logs. */ archiverPollingIntervalMS?: number; @@ -44,6 +47,11 @@ export const archiverConfigMappings: ConfigMappingsType = { description: 'URL for an archiver service. If set, will return an archiver client as opposed to starting a new one.', }, + l1ConsensusClientUrl: { + env: 'L1_CONSENSUS_CLIENT_URL', + description: 'URL for an L1 consensus client.', + parseEnv: (val: string) => (val ? val : 'http://localhost:5052'), + }, archiverPollingIntervalMS: { env: 'ARCHIVER_POLLING_INTERVAL_MS', description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.', diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index b1d0c8ce21f..6fe16439066 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -52,6 +52,7 @@ export type EnvVar = | 'GOVERNANCE_PROPOSER_PAYLOAD_ADDRESS' | 'INBOX_CONTRACT_ADDRESS' | 'L1_CHAIN_ID' + | 'L1_CONSENSUS_CLIENT_URL' | 'L1_PRIVATE_KEY' | 'L2_QUEUE_SIZE' | 'LOG_ELAPSED_TIME' From 5538f8cfc94f617a5604706b53357f9018af1096 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Mon, 16 Dec 2024 19:48:56 +0000 Subject: [PATCH 07/10] chore: remove spurious echo (#10774) --- scripts/tmux_split_args.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/tmux_split_args.sh b/scripts/tmux_split_args.sh index ef69695682d..4571c858cc9 100755 --- a/scripts/tmux_split_args.sh +++ b/scripts/tmux_split_args.sh @@ -23,8 +23,6 @@ tmux new-session -d -s "$session_name" -e LOG_LEVEL=${LOG_LEVEL:-"debug"} \ -e OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-} \ -e LOG_JSON=${LOG_JSON:-} -echo "DONE" - shift 1 commands=("$@") @@ -34,7 +32,7 @@ tmux set-option -t "$session_name" pane-border-format "#{pane_title}" base_index=$(tmux show-options -g base-index 2>/dev/null | awk '{print $2}') base_index=${base_index:-0} -echo "base_index=$base_index" +echo "Using tmux base_index=$base_index" # Create the necessary number of panes and set titles num_commands=${#commands[@]} From c8e77639bf7f30dffe98ae335d5d1137da838e55 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Mon, 16 Dec 2024 17:32:06 -0300 Subject: [PATCH 08/10] chore: Remove default export for noir contracts js (#10762) To reduce long loading times when importing contract artifacts from noir contracts js, we remove all artifacts from the default export, and instead just expose a list of contract names available. To import a given artifact, we now use the corresponding export, so we only need to load that artifact and not all. --- docs/docs/migration_notes.md | 10 ++++++- yarn-project/archiver/src/factory.ts | 3 +- yarn-project/bot/src/bot.ts | 3 +- yarn-project/bot/src/factory.ts | 2 +- yarn-project/bot/src/utils.ts | 2 +- .../src/cmds/contracts/inspect_contract.ts | 16 +++++++--- .../cli/src/cmds/devnet/bootstrap_network.ts | 14 ++++++--- .../cli/src/cmds/misc/example_contracts.ts | 11 ++++--- .../cli/src/cmds/misc/setup_contracts.ts | 2 +- yarn-project/cli/src/utils/aztec.ts | 29 +++++++++---------- .../src/benchmarks/bench_prover.test.ts | 5 +++- .../src/benchmarks/bench_tx_size_fees.test.ts | 4 ++- .../end-to-end/src/devnet/e2e_smoke.test.ts | 3 +- .../end-to-end/src/e2e_2_pxes.test.ts | 4 ++- yarn-project/end-to-end/src/e2e_amm.test.ts | 3 +- .../end-to-end/src/e2e_authwit.test.ts | 3 +- .../end-to-end/src/e2e_avm_simulator.test.ts | 3 +- .../blacklist_token_contract_test.ts | 4 ++- .../end-to-end/src/e2e_block_building.test.ts | 2 +- .../end-to-end/src/e2e_cheat_codes.test.ts | 2 +- .../cross_chain_messaging_test.ts | 3 +- .../l1_to_l2.test.ts | 2 +- .../l2_to_l1.test.ts | 2 +- .../src/e2e_crowdfunding_and_claim.test.ts | 2 +- .../contract_class_registration.test.ts | 3 +- .../e2e_deploy_contract/deploy_method.test.ts | 3 +- .../src/e2e_deploy_contract/deploy_test.ts | 2 +- .../src/e2e_deploy_contract/legacy.test.ts | 2 +- .../private_initialization.test.ts | 2 +- .../end-to-end/src/e2e_event_logs.test.ts | 2 +- .../src/e2e_fees/account_init.test.ts | 4 ++- .../src/e2e_fees/dapp_subscription.test.ts | 10 +++---- .../end-to-end/src/e2e_fees/failures.test.ts | 3 +- .../src/e2e_fees/fee_juice_payments.test.ts | 3 +- .../end-to-end/src/e2e_fees/fees_test.ts | 12 ++++---- .../src/e2e_fees/gas_estimation.test.ts | 3 +- .../src/e2e_fees/private_payments.test.ts | 3 +- .../src/e2e_fees/public_payments.test.ts | 3 +- yarn-project/end-to-end/src/e2e_keys.test.ts | 2 +- .../src/e2e_lending_contract.test.ts | 4 ++- .../src/e2e_max_block_number.test.ts | 2 +- .../src/e2e_nested_contract/importer.test.ts | 3 +- .../manual_private_enqueue.test.ts | 3 +- .../nested_contract_test.ts | 3 +- yarn-project/end-to-end/src/e2e_nft.test.ts | 2 +- .../end-to-end/src/e2e_note_getter.test.ts | 3 +- .../end-to-end/src/e2e_outbox.test.ts | 2 +- .../end-to-end/src/e2e_p2p/p2p_network.ts | 2 +- yarn-project/end-to-end/src/e2e_p2p/shared.ts | 2 +- .../src/e2e_prover/e2e_prover_test.ts | 2 +- .../end-to-end/src/e2e_state_vars.test.ts | 3 +- .../end-to-end/src/e2e_static_calls.test.ts | 3 +- .../end-to-end/src/e2e_synching.test.ts | 4 ++- .../private_transfer_recursion.test.ts | 2 +- .../e2e_token_contract/token_contract_test.ts | 3 +- .../src/e2e_token_contract/transfer.test.ts | 2 +- .../end-to-end/src/fixtures/token_utils.ts | 2 +- ...akey_e2e_inclusion_proofs_contract.test.ts | 2 +- .../e2e_prover_coordination.test.ts | 2 +- .../e2e_public_testnet_transfer.test.ts | 2 +- .../end-to-end/src/sample-dapp/deploy.mjs | 3 +- .../src/shared/gas_portal_test_harness.ts | 2 +- .../end-to-end/src/spartan/4epochs.test.ts | 2 +- .../src/spartan/setup_test_wallets.ts | 2 +- .../end-to-end/src/spartan/transfer.test.ts | 2 +- yarn-project/noir-contracts.js/package.json | 2 +- .../scripts/generate-types.sh | 5 +++- .../src/tx_validator/gas_validator.test.ts | 2 +- .../simulator/src/avm/fixtures/index.ts | 2 +- .../src/client/private_execution.test.ts | 14 ++++----- .../simulator/src/client/simulator.test.ts | 2 +- .../simulator/src/public/fixtures/index.ts | 2 +- 72 files changed, 171 insertions(+), 113 deletions(-) diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index 348697ad9eb..5e376e4909a 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -6,6 +6,12 @@ keywords: [sandbox, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## 0.68.0 + +### Noir contracts package no longer exposes artifacts as default export + +To reduce loading times, the package `@aztec/noir-contracts.js` no longer exposes all artifacts as its default export. Instead, it exposes a `ContractNames` variable with the list of all contract names available. To import a given artifact, use the corresponding export, such as `@aztec/noir-contracts.js/FPC`. + ## 0.67.0 ### L2 Gas limit of 6M enforced for public portion of TX @@ -24,10 +30,11 @@ The `Header` struct has been renamed to `BlockHeader`, and the `get_header()` fa ### Outgoing Events removed Previously, every event which was emitted included: + - Incoming Header (to convey the app contract address to the recipient) - Incoming Ciphertext (to convey the note contents to the recipient) - Outgoing Header (served as a backup, to convey the app contract address to the "outgoing viewer" - most likely the sender) -- Outgoing Ciphertext (served as a backup, encrypting the summetric key of the incoming ciphertext to the "outgoing viewer" - most likely the sender) +- Outgoing Ciphertext (served as a backup, encrypting the symmetric key of the incoming ciphertext to the "outgoing viewer" - most likely the sender) The latter two have been removed from the `.emit()` functions, so now only an Incoming Header and Incoming Ciphertext will be emitted. @@ -43,6 +50,7 @@ The `getOutgoingNotes` function is removed from the PXE interface. Some aztec.nr library methods' arguments are simplified to remove an `outgoing_viewer` parameter. E.g. `ValueNote::increment`, `ValueNote::decrement`, `ValueNote::decrement_by_at_most`, `EasyPrivateUint::add`, `EasyPrivateUint::sub`. Further changes are planned, so that: + - Outgoing ciphertexts (or any kind of abstract ciphertext) can be emitted by a contract, and on the other side discovered and then processed by the contract. - Headers will be removed, due to the new tagging scheme. diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index 45ab9705429..afeb43cf7cb 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -8,7 +8,8 @@ import { createLogger } from '@aztec/foundation/log'; import { type Maybe } from '@aztec/foundation/types'; import { type DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb'; -import { TokenBridgeContractArtifact, TokenContractArtifact } from '@aztec/noir-contracts.js'; +import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; +import { TokenBridgeContractArtifact } from '@aztec/noir-contracts.js/TokenBridge'; import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index 0cf02b758fe..3a9c9e48871 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -10,7 +10,8 @@ import { import { type AztecNode, type FunctionCall, type PXE } from '@aztec/circuit-types'; import { Gas } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; -import { type EasyPrivateTokenContract, type TokenContract } from '@aztec/noir-contracts.js'; +import { type EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken'; +import { type TokenContract } from '@aztec/noir-contracts.js/Token'; import { type BotConfig } from './config.js'; import { BotFactory } from './factory.js'; diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 03098a3095c..440b219a3f5 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -9,7 +9,7 @@ import { } from '@aztec/aztec.js'; import { type AztecNode, type FunctionCall, type PXE } from '@aztec/circuit-types'; import { Fr, deriveSigningKey } from '@aztec/circuits.js'; -import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js'; +import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { type BotConfig, SupportedTokenContracts } from './config.js'; diff --git a/yarn-project/bot/src/utils.ts b/yarn-project/bot/src/utils.ts index 0bc402c6a34..9a463598263 100644 --- a/yarn-project/bot/src/utils.ts +++ b/yarn-project/bot/src/utils.ts @@ -1,5 +1,5 @@ import { type AztecAddress } from '@aztec/circuits.js'; -import { type EasyPrivateTokenContract } from '@aztec/noir-contracts.js'; +import { type EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken'; import { type TokenContract } from '@aztec/noir-contracts.js/Token'; /** diff --git a/yarn-project/cli/src/cmds/contracts/inspect_contract.ts b/yarn-project/cli/src/cmds/contracts/inspect_contract.ts index f1d4bbbb8ed..0c79238ae00 100644 --- a/yarn-project/cli/src/cmds/contracts/inspect_contract.ts +++ b/yarn-project/cli/src/cmds/contracts/inspect_contract.ts @@ -25,10 +25,18 @@ export async function inspectContract(contractArtifactFile: string, debugLogger: log(`\tprivate function tree root: ${contractClass.privateFunctionsRoot.toString()}`); log(`\tpublic bytecode commitment: ${contractClass.publicBytecodeCommitment.toString()}`); log(`\tpublic bytecode length: ${contractClass.packedBytecode.length} bytes (${bytecodeLengthInFields} fields)`); - log(`\nExternal functions:`); - contractFns.filter(f => !f.isInternal).forEach(f => logFunction(f, log)); - log(`\nInternal functions:`); - contractFns.filter(f => f.isInternal).forEach(f => logFunction(f, log)); + + const externalFunctions = contractFns.filter(f => !f.isInternal); + if (externalFunctions.length > 0) { + log(`\nExternal functions:`); + externalFunctions.forEach(f => logFunction(f, log)); + } + + const internalFunctions = contractFns.filter(f => f.isInternal); + if (internalFunctions.length > 0) { + log(`\nInternal functions:`); + internalFunctions.forEach(f => logFunction(f, log)); + } } function logFunction(fn: FunctionArtifact, log: LogFn) { diff --git a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts index 462ae4649bd..9ba166fad9d 100644 --- a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts +++ b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts @@ -161,7 +161,10 @@ async function deployToken( ): Promise<{ token: ContractDeploymentInfo; bridge: ContractDeploymentInfo }> { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const { TokenContract, TokenBridgeContract } = await import('@aztec/noir-contracts.js'); + const { TokenContract } = await import('@aztec/noir-contracts.js/Token'); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment + const { TokenBridgeContract } = await import('@aztec/noir-contracts.js/TokenBridge'); const devCoin = await TokenContract.deploy(wallet, wallet.getAddress(), 'DevCoin', 'DEV', 18) .send({ universalDeploy: true }) .deployed(waitOpts); @@ -223,7 +226,7 @@ async function deployFPC( ): Promise { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const { FPCContract } = await import('@aztec/noir-contracts.js'); + const { FPCContract } = await import('@aztec/noir-contracts.js/FPC'); const fpc = await FPCContract.deploy(wallet, tokenAddress, feeRecipient) .send({ universalDeploy: true }) .deployed(waitOpts); @@ -238,7 +241,7 @@ async function deployFPC( async function deployCounter(wallet: Wallet): Promise { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const { CounterContract } = await import('@aztec/noir-contracts.js'); + const { CounterContract } = await import('@aztec/noir-contracts.js/Counter'); const counter = await CounterContract.deploy(wallet, 1, wallet.getAddress()) .send({ universalDeploy: true }) .deployed(waitOpts); @@ -260,7 +263,10 @@ async function fundFPC( ) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const { FeeJuiceContract, CounterContract } = await import('@aztec/noir-contracts.js'); + const { FeeJuiceContract } = await import('@aztec/noir-contracts.js/FeeJuice'); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment + const { CounterContract } = await import('@aztec/noir-contracts.js/Counter'); const { protocolContractAddresses: { feeJuice }, } = await wallet.getPXEInfo(); diff --git a/yarn-project/cli/src/cmds/misc/example_contracts.ts b/yarn-project/cli/src/cmds/misc/example_contracts.ts index c2f9d172b45..1c012f59f0c 100644 --- a/yarn-project/cli/src/cmds/misc/example_contracts.ts +++ b/yarn-project/cli/src/cmds/misc/example_contracts.ts @@ -1,9 +1,12 @@ import { type LogFn } from '@aztec/foundation/log'; -import { getExampleContractArtifacts } from '../../utils/aztec.js'; +import { getExampleContractNames } from '../../utils/aztec.js'; export async function exampleContracts(log: LogFn) { - const abisList = await getExampleContractArtifacts(); - const names = Object.keys(abisList).filter(name => name !== 'AvmTestContractArtifact'); - names.forEach(name => log(name)); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment + return (await getExampleContractNames()) + .filter(name => name !== 'AvmTest') + .sort() + .forEach(name => log(name)); } diff --git a/yarn-project/cli/src/cmds/misc/setup_contracts.ts b/yarn-project/cli/src/cmds/misc/setup_contracts.ts index 70ea4b79dc6..d2d13c5ad1c 100644 --- a/yarn-project/cli/src/cmds/misc/setup_contracts.ts +++ b/yarn-project/cli/src/cmds/misc/setup_contracts.ts @@ -14,7 +14,7 @@ export async function setupCanonicalL2FeeJuice( ) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const { FeeJuiceContract } = await import('@aztec/noir-contracts.js'); + const { FeeJuiceContract } = await import('@aztec/noir-contracts.js/FeeJuice'); const feeJuiceContract = await FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, deployer); diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index 97759f65ec5..9949adb635a 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -23,13 +23,6 @@ import { import { encodeArgs } from './encoding.js'; -/** - * Helper type to dynamically import contracts. - */ -interface ArtifactsType { - [key: string]: ContractArtifact; -} - /** * Helper to get an ABI function or throw error if it doesn't exist. * @param artifact - Contract's build artifact in JSON format. @@ -98,13 +91,13 @@ export async function setAssumeProvenThrough( /** * Gets all contracts available in \@aztec/noir-contracts.js. - * @returns The contract ABIs. + * @returns The contract names. */ -export async function getExampleContractArtifacts(): Promise { +export async function getExampleContractNames(): Promise { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment - const imports = await import('@aztec/noir-contracts.js'); - return Object.fromEntries(Object.entries(imports).filter(([key]) => key.endsWith('Artifact'))) as any; + const { ContractNames } = await import('@aztec/noir-contracts.js'); + return ContractNames; } /** @@ -114,11 +107,17 @@ export async function getExampleContractArtifacts(): Promise { */ export async function getContractArtifact(fileDir: string, log: LogFn) { // first check if it's a noir-contracts example - const artifacts = await getExampleContractArtifacts(); - for (const key of [fileDir, fileDir + 'Artifact', fileDir + 'ContractArtifact']) { - if (artifacts[key]) { - return artifacts[key] as ContractArtifact; + const allNames = await getExampleContractNames(); + const contractName = fileDir.replace(/Contract(Artifact)?$/, ''); + if (allNames.includes(contractName)) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment + const imported = await import(`@aztec/noir-contracts.js/${contractName}`); + const artifact = imported[`${contractName}ContractArtifact`] as ContractArtifact; + if (!artifact) { + throw Error(`Could not import ${contractName}ContractArtifact from @aztec/noir-contracts.js/${contractName}`); } + return artifact; } let contents: string; diff --git a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts index 49a09404359..5cfda3ff9d6 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts @@ -3,7 +3,10 @@ import { PublicFeePaymentMethod, TxStatus, sleep } from '@aztec/aztec.js'; import { type AccountWallet } from '@aztec/aztec.js/wallet'; import { BBCircuitVerifier } from '@aztec/bb-prover'; import { CompleteAddress, FEE_FUNDING_FOR_TESTER_ACCOUNT, Fq, Fr, GasSettings } from '@aztec/circuits.js'; -import { FPCContract, FeeJuiceContract, TestContract, TokenContract } from '@aztec/noir-contracts.js'; +import { FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { type PXEService, type PXEServiceConfig, createPXEService } from '@aztec/pxe'; diff --git a/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts index 63f485f9c1a..a998193f7f8 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts @@ -8,7 +8,9 @@ import { TxStatus, } from '@aztec/aztec.js'; import { FEE_FUNDING_FOR_TESTER_ACCOUNT, GasSettings } from '@aztec/circuits.js'; -import { FPCContract, FeeJuiceContract, TokenContract } from '@aztec/noir-contracts.js'; +import { FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts index 1a12baf2596..776b09f6865 100644 --- a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts +++ b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts @@ -19,7 +19,8 @@ import { deriveSigningKey } from '@aztec/circuits.js'; import { createNamespacedSafeJsonRpcServer, startHttpRpcServer } from '@aztec/foundation/json-rpc/server'; import { type Logger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; -import { FeeJuiceContract, TestContract } from '@aztec/noir-contracts.js'; +import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import getPort from 'get-port'; import { exec } from 'node:child_process'; diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index eaa70fa10ff..d52f3494ecd 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -10,7 +10,9 @@ import { type Wallet, sleep, } from '@aztec/aztec.js'; -import { ChildContract, TestContract, TokenContract } from '@aztec/noir-contracts.js'; +import { ChildContract } from '@aztec/noir-contracts.js/Child'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { expect, jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_amm.test.ts b/yarn-project/end-to-end/src/e2e_amm.test.ts index 476bfb8be8b..9e87c17cc45 100644 --- a/yarn-project/end-to-end/src/e2e_amm.test.ts +++ b/yarn-project/end-to-end/src/e2e_amm.test.ts @@ -1,5 +1,6 @@ import { type AccountWallet, Fr, type Logger, type Wallet } from '@aztec/aztec.js'; -import { AMMContract, type TokenContract } from '@aztec/noir-contracts.js'; +import { AMMContract } from '@aztec/noir-contracts.js/AMM'; +import { type TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_authwit.test.ts b/yarn-project/end-to-end/src/e2e_authwit.test.ts index b8e19acb6f1..71d4331f054 100644 --- a/yarn-project/end-to-end/src/e2e_authwit.test.ts +++ b/yarn-project/end-to-end/src/e2e_authwit.test.ts @@ -1,5 +1,6 @@ import { type AccountWallet, Fr, computeAuthWitMessageHash, computeInnerAuthWitHash } from '@aztec/aztec.js'; -import { AuthRegistryContract, AuthWitTestContract } from '@aztec/noir-contracts.js'; +import { AuthRegistryContract } from '@aztec/noir-contracts.js/AuthRegistry'; +import { AuthWitTestContract } from '@aztec/noir-contracts.js/AuthWitTest'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 8ec9c96f24d..b8e26d7f97e 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -1,5 +1,6 @@ import { type AccountWallet, AztecAddress, BatchCall, Fr, TxStatus } from '@aztec/aztec.js'; -import { AvmInitializerTestContract, AvmTestContract } from '@aztec/noir-contracts.js'; +import { AvmInitializerTestContract } from '@aztec/noir-contracts.js/AvmInitializerTest'; +import { AvmTestContract } from '@aztec/noir-contracts.js/AvmTest'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts index d7dc45a2ca6..da4af23cdcc 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts @@ -10,7 +10,9 @@ import { computeSecretHash, createLogger, } from '@aztec/aztec.js'; -import { DocsExampleContract, TokenBlacklistContract, type TokenContract } from '@aztec/noir-contracts.js'; +import { DocsExampleContract } from '@aztec/noir-contracts.js/DocsExample'; +import { type TokenContract } from '@aztec/noir-contracts.js/Token'; +import { TokenBlacklistContract } from '@aztec/noir-contracts.js/TokenBlacklist'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 29635955b8b..71ae0eeab25 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -20,7 +20,7 @@ import { import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { times } from '@aztec/foundation/collection'; import { poseidon2Hash } from '@aztec/foundation/crypto'; -import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; +import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulTest'; import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 6dc08e64aee..280a8e5b743 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -1,6 +1,6 @@ import { type AztecAddress, type CheatCodes, EthAddress, Fr, type Wallet } from '@aztec/aztec.js'; import { RollupAbi } from '@aztec/l1-artifacts'; -import { TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { type Account, diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts index 2ee9eee8b68..b0d724f44f5 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts @@ -12,7 +12,8 @@ import { } from '@aztec/aztec.js'; import { createL1Clients } from '@aztec/ethereum'; import { InboxAbi, OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; -import { TokenBridgeContract, TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; +import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge'; import { type Chain, type HttpTransport, type PublicClient, getContract } from 'viem'; diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index 7ff83ac3156..8c67dd634ba 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -1,5 +1,5 @@ import { type AztecAddress, Fr, generateClaimSecret } from '@aztec/aztec.js'; -import { TestContract } from '@aztec/noir-contracts.js'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { sendL1ToL2Message } from '../fixtures/l1_to_l2_messaging.js'; import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts index 60eb205af6e..bbe6be8e993 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts @@ -1,7 +1,7 @@ import { Fr } from '@aztec/aztec.js'; import { sha256ToField } from '@aztec/foundation/crypto'; import { OutboxAbi } from '@aztec/l1-artifacts'; -import { TestContract } from '@aztec/noir-contracts.js'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { type Hex, decodeEventLog, getContract } from 'viem'; diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index 4dcc22cad57..0ac2e55f332 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -12,9 +12,9 @@ import { deriveKeys, } from '@aztec/aztec.js'; import { GasSettings, TxContext, computePartialAddress } from '@aztec/circuits.js'; -import { InclusionProofsContract } from '@aztec/noir-contracts.js'; import { ClaimContract } from '@aztec/noir-contracts.js/Claim'; import { CrowdfundingContract } from '@aztec/noir-contracts.js/Crowdfunding'; +import { InclusionProofsContract } from '@aztec/noir-contracts.js/InclusionProofs'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index 4ef0d5014d8..3cb28f8d7f2 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -23,8 +23,9 @@ import { import { type ContractClassIdPreimage, PublicKeys, computeContractClassId } from '@aztec/circuits.js'; import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { writeTestData } from '@aztec/foundation/testing'; -import { StatefulTestContract, TokenContractArtifact } from '@aztec/noir-contracts.js'; +import { StatefulTestContract } from '@aztec/noir-contracts.js/StatefulTest'; import { TestContract } from '@aztec/noir-contracts.js/Test'; +import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; import { DUPLICATE_NULLIFIER_ERROR } from '../fixtures/fixtures.js'; import { DeployTest, type StatefulContractCtorArgs } from './deploy_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index a4d305c089e..5f2fe0781b4 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -1,6 +1,7 @@ import { getDeployedTestAccountsWallets } from '@aztec/accounts/testing'; import { AztecAddress, type Logger, type PXE, type Wallet, createPXEClient, makeFetch } from '@aztec/aztec.js'; -import { CounterContract, StatefulTestContract } from '@aztec/noir-contracts.js'; +import { CounterContract } from '@aztec/noir-contracts.js/Counter'; +import { StatefulTestContract } from '@aztec/noir-contracts.js/StatefulTest'; import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts index 2632b9e4d6d..4dbbc665577 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts @@ -13,7 +13,7 @@ import { createLogger, getContractInstanceFromDeployParams, } from '@aztec/aztec.js'; -import { type StatefulTestContract } from '@aztec/noir-contracts.js'; +import { type StatefulTestContract } from '@aztec/noir-contracts.js/StatefulTest'; import { type ISnapshotManager, addAccounts, createSnapshotManager } from '../fixtures/snapshot_manager.js'; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts index 1187950bbe9..81603e318ea 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts @@ -8,7 +8,7 @@ import { type Wallet, getContractInstanceFromDeployParams, } from '@aztec/aztec.js'; -import { StatefulTestContract } from '@aztec/noir-contracts.js'; +import { StatefulTestContract } from '@aztec/noir-contracts.js/StatefulTest'; import { TestContractArtifact } from '@aztec/noir-contracts.js/Test'; import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts index 8978e4f6a62..788ba730118 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts @@ -1,6 +1,6 @@ import { BatchCall, Fr, type Logger, type PXE, SignerlessWallet, type Wallet } from '@aztec/aztec.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; -import { StatefulTestContract } from '@aztec/noir-contracts.js'; +import { StatefulTestContract } from '@aztec/noir-contracts.js/StatefulTest'; import { TestContract } from '@aztec/noir-contracts.js/Test'; import { DeployTest, type StatefulContractCtorArgs } from './deploy_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index cdaf45e61d6..914b99ad8ac 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -9,7 +9,7 @@ import { import { EventSelector } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { type Tuple } from '@aztec/foundation/serialize'; -import { type ExampleEvent0, type ExampleEvent1, TestLogContract } from '@aztec/noir-contracts.js'; +import { type ExampleEvent0, type ExampleEvent1, TestLogContract } from '@aztec/noir-contracts.js/TestLog'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts index 820d2a51ea5..f4464e972c4 100644 --- a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts @@ -20,7 +20,9 @@ import { Fq, type GasSettings, } from '@aztec/circuits.js'; -import { type TokenContract as BananaCoin, type FPCContract, SchnorrAccountContract } from '@aztec/noir-contracts.js'; +import { type FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { SchnorrAccountContract } from '@aztec/noir-contracts.js/SchnorrAccount'; +import { type TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts index 45665cfc883..262272d194c 100644 --- a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts @@ -9,12 +9,10 @@ import { PublicFeePaymentMethod, } from '@aztec/aztec.js'; import { FEE_FUNDING_FOR_TESTER_ACCOUNT, type GasSettings } from '@aztec/circuits.js'; -import { - type AppSubscriptionContract, - type TokenContract as BananaCoin, - type CounterContract, - type FPCContract, -} from '@aztec/noir-contracts.js'; +import { type AppSubscriptionContract } from '@aztec/noir-contracts.js/AppSubscription'; +import { type CounterContract } from '@aztec/noir-contracts.js/Counter'; +import { type FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { type TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { expectMapping, expectMappingDelta } from '../fixtures/utils.js'; import { FeesTest } from './fees_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts index 57a7559e12f..26ce82243ed 100644 --- a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts @@ -10,7 +10,8 @@ import { } from '@aztec/aztec.js'; import { Gas, GasSettings } from '@aztec/circuits.js'; import { FunctionType } from '@aztec/foundation/abi'; -import { type TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js'; +import { type FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { type TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { expectMapping } from '../fixtures/utils.js'; import { FeesTest } from './fees_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts index 112425fafaf..8183915875c 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts @@ -5,7 +5,8 @@ import { FeeJuicePaymentMethodWithClaim, } from '@aztec/aztec.js'; import { FEE_FUNDING_FOR_TESTER_ACCOUNT, type GasSettings } from '@aztec/circuits.js'; -import { type TokenContract as BananaCoin, type FeeJuiceContract } from '@aztec/noir-contracts.js'; +import { type FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; +import { type TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { FeesTest } from './fees_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index ffbf4d01f66..427816c6d9a 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -13,13 +13,11 @@ import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; import { EthAddress, FEE_FUNDING_FOR_TESTER_ACCOUNT, GasSettings, computePartialAddress } from '@aztec/circuits.js'; import { createL1Clients } from '@aztec/ethereum'; import { TestERC20Abi } from '@aztec/l1-artifacts'; -import { - AppSubscriptionContract, - TokenContract as BananaCoin, - CounterContract, - FPCContract, - FeeJuiceContract, -} from '@aztec/noir-contracts.js'; +import { AppSubscriptionContract } from '@aztec/noir-contracts.js/AppSubscription'; +import { CounterContract } from '@aztec/noir-contracts.js/Counter'; +import { FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; +import { TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice'; diff --git a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts index f3655716c22..522004039e4 100644 --- a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -8,7 +8,8 @@ import { } from '@aztec/aztec.js'; import { GasSettings } from '@aztec/circuits.js'; import { type Logger } from '@aztec/foundation/log'; -import { TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js'; +import { type FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { inspect } from 'util'; diff --git a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts index fd59e1f2140..e9518e69f08 100644 --- a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts @@ -1,6 +1,7 @@ import { type AccountWallet, type AztecAddress, BatchCall, PrivateFeePaymentMethod, sleep } from '@aztec/aztec.js'; import { GasSettings } from '@aztec/circuits.js'; -import { type TokenContract as BananaCoin, FPCContract } from '@aztec/noir-contracts.js'; +import { FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { type TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { expectMapping } from '../fixtures/utils.js'; import { FeesTest } from './fees_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts index 83f4a4ef68b..22e4fbcb46a 100644 --- a/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts @@ -1,6 +1,7 @@ import { type AccountWallet, type AztecAddress, PublicFeePaymentMethod } from '@aztec/aztec.js'; import { GasSettings } from '@aztec/circuits.js'; -import { type TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js'; +import { type FPCContract } from '@aztec/noir-contracts.js/FPC'; +import { type TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token'; import { expectMapping } from '../fixtures/utils.js'; import { FeesTest } from './fees_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_keys.test.ts b/yarn-project/end-to-end/src/e2e_keys.test.ts index 7cab699963e..c114ec46a7c 100644 --- a/yarn-project/end-to-end/src/e2e_keys.test.ts +++ b/yarn-project/end-to-end/src/e2e_keys.test.ts @@ -19,7 +19,7 @@ import { } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; -import { TestContract } from '@aztec/noir-contracts.js'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 6c0df07c5da..0b64d5aac47 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -1,6 +1,8 @@ import { type AccountWallet, type CheatCodes, type DeployL1Contracts, Fr, type Logger } from '@aztec/aztec.js'; import { RollupAbi } from '@aztec/l1-artifacts'; -import { LendingContract, PriceFeedContract, TokenContract } from '@aztec/noir-contracts.js'; +import { LendingContract } from '@aztec/noir-contracts.js/Lending'; +import { PriceFeedContract } from '@aztec/noir-contracts.js/PriceFeed'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { afterAll, jest } from '@jest/globals'; import { getContract } from 'viem'; diff --git a/yarn-project/end-to-end/src/e2e_max_block_number.test.ts b/yarn-project/end-to-end/src/e2e_max_block_number.test.ts index 0c05164a2c1..b83d613ad36 100644 --- a/yarn-project/end-to-end/src/e2e_max_block_number.test.ts +++ b/yarn-project/end-to-end/src/e2e_max_block_number.test.ts @@ -1,5 +1,5 @@ import { Fr, type PXE, type Wallet } from '@aztec/aztec.js'; -import { TestContract } from '@aztec/noir-contracts.js'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { setup } from './fixtures/utils.js'; diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts index e11ce439df9..c4f49e20cd3 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts @@ -1,4 +1,5 @@ -import { ImportTestContract, TestContract } from '@aztec/noir-contracts.js'; +import { ImportTestContract } from '@aztec/noir-contracts.js/ImportTest'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { NestedContractTest } from './nested_contract_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts index a0ba5c148c0..57cd7354c6a 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts @@ -1,5 +1,6 @@ import { type AztecAddress, Fr } from '@aztec/aztec.js'; -import { ChildContract, ParentContract } from '@aztec/noir-contracts.js'; +import { ChildContract } from '@aztec/noir-contracts.js/Child'; +import { ParentContract } from '@aztec/noir-contracts.js/Parent'; import { NestedContractTest } from './nested_contract_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts index 11db0cb233c..733ecde7318 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts @@ -1,6 +1,7 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type AccountWallet, type CompleteAddress, type Logger, type PXE, createLogger } from '@aztec/aztec.js'; -import { ChildContract, ParentContract } from '@aztec/noir-contracts.js'; +import { ChildContract } from '@aztec/noir-contracts.js/Child'; +import { ParentContract } from '@aztec/noir-contracts.js/Parent'; import { type ISnapshotManager, diff --git a/yarn-project/end-to-end/src/e2e_nft.test.ts b/yarn-project/end-to-end/src/e2e_nft.test.ts index e79ffaed49f..b39228d5643 100644 --- a/yarn-project/end-to-end/src/e2e_nft.test.ts +++ b/yarn-project/end-to-end/src/e2e_nft.test.ts @@ -1,5 +1,5 @@ import { type AccountWallet, AztecAddress, Fr } from '@aztec/aztec.js'; -import { NFTContract } from '@aztec/noir-contracts.js'; +import { NFTContract } from '@aztec/noir-contracts.js/NFT'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_note_getter.test.ts b/yarn-project/end-to-end/src/e2e_note_getter.test.ts index e7aea9067a0..86c91c658af 100644 --- a/yarn-project/end-to-end/src/e2e_note_getter.test.ts +++ b/yarn-project/end-to-end/src/e2e_note_getter.test.ts @@ -1,5 +1,6 @@ import { type AztecAddress, Comparator, Fr, type Wallet } from '@aztec/aztec.js'; -import { DocsExampleContract, TestContract } from '@aztec/noir-contracts.js'; +import { DocsExampleContract } from '@aztec/noir-contracts.js/DocsExample'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { setup } from './fixtures/utils.js'; diff --git a/yarn-project/end-to-end/src/e2e_outbox.test.ts b/yarn-project/end-to-end/src/e2e_outbox.test.ts index a3eb8fb43a6..dfcc7979543 100644 --- a/yarn-project/end-to-end/src/e2e_outbox.test.ts +++ b/yarn-project/end-to-end/src/e2e_outbox.test.ts @@ -11,7 +11,7 @@ import { sha256ToField } from '@aztec/foundation/crypto'; import { truncateAndPad } from '@aztec/foundation/serialize'; import { OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { SHA256 } from '@aztec/merkle-tree'; -import { TestContract } from '@aztec/noir-contracts.js'; +import { TestContract } from '@aztec/noir-contracts.js/Test'; import { beforeEach, describe, expect, it } from '@jest/globals'; import { decodeEventLog, getContract } from 'viem'; diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index a218e4e7fe3..c033061b6d9 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -4,7 +4,7 @@ import { type AccountWalletWithSecretKey } from '@aztec/aztec.js'; import { EthCheatCodes, MINIMUM_STAKE, getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; -import { SpamContract } from '@aztec/noir-contracts.js'; +import { SpamContract } from '@aztec/noir-contracts.js/Spam'; import { type BootstrapNode } from '@aztec/p2p'; import { createBootstrapNodeFromPrivateKey } from '@aztec/p2p/mocks'; diff --git a/yarn-project/end-to-end/src/e2e_p2p/shared.ts b/yarn-project/end-to-end/src/e2e_p2p/shared.ts index a3fb06b49ae..5ff6a421157 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/shared.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/shared.ts @@ -3,7 +3,7 @@ import { type AztecNodeService } from '@aztec/aztec-node'; import { type Logger, type SentTx } from '@aztec/aztec.js'; import { CompleteAddress, TxStatus } from '@aztec/aztec.js'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; -import { type SpamContract } from '@aztec/noir-contracts.js'; +import { type SpamContract } from '@aztec/noir-contracts.js/Spam'; import { type PXEService, createPXEService, getPXEServiceConfig as getRpcConfig } from '@aztec/pxe'; import { type NodeContext } from '../fixtures/setup_p2p_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index c6d0bab98a7..aac3af2db0a 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -23,7 +23,7 @@ import { import { compileContract } from '@aztec/ethereum'; import { Buffer32 } from '@aztec/foundation/buffer'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; -import { TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { type ProverNode, type ProverNodeConfig, createProverNode } from '@aztec/prover-node'; import { type PXEService } from '@aztec/pxe'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 7a47098bc32..a32d0985945 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -1,5 +1,6 @@ import { BatchCall, Fr, type PXE, type Wallet } from '@aztec/aztec.js'; -import { AuthContract, DocsExampleContract } from '@aztec/noir-contracts.js'; +import { AuthContract } from '@aztec/noir-contracts.js/Auth'; +import { DocsExampleContract } from '@aztec/noir-contracts.js/DocsExample'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index 1d670377c1a..b8d806de1c0 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -1,5 +1,6 @@ import { type AztecAddress, type Wallet } from '@aztec/aztec.js'; -import { StaticChildContract, StaticParentContract } from '@aztec/noir-contracts.js'; +import { StaticChildContract } from '@aztec/noir-contracts.js/StaticChild'; +import { StaticParentContract } from '@aztec/noir-contracts.js/StaticParent'; import { STATIC_CALL_STATE_MODIFICATION_ERROR, STATIC_CONTEXT_ASSERTION_ERROR } from './fixtures/fixtures.js'; import { setup } from './fixtures/utils.js'; diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index d4a5563fa37..50857c56baf 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -51,7 +51,9 @@ import { type AztecAddress } from '@aztec/circuits.js'; import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { Timer } from '@aztec/foundation/timer'; import { RollupAbi } from '@aztec/l1-artifacts'; -import { SchnorrHardcodedAccountContract, SpamContract, TokenContract } from '@aztec/noir-contracts.js'; +import { SchnorrHardcodedAccountContract } from '@aztec/noir-contracts.js/SchnorrHardcodedAccount'; +import { SpamContract } from '@aztec/noir-contracts.js/Spam'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { type PXEService } from '@aztec/pxe'; import { L1Publisher } from '@aztec/sequencer-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; diff --git a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts index 7039882ac10..43e789c6887 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts @@ -1,5 +1,5 @@ import { BatchCall } from '@aztec/aztec.js'; -import { TokenContract, type Transfer } from '@aztec/noir-contracts.js'; +import { TokenContract, type Transfer } from '@aztec/noir-contracts.js/Token'; import { TokenContractTest } from './token_contract_test.js'; diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index 9453ef7692f..98b117652c7 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -1,6 +1,7 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type AccountWallet, type CompleteAddress, type Logger, createLogger } from '@aztec/aztec.js'; -import { DocsExampleContract, TokenContract } from '@aztec/noir-contracts.js'; +import { DocsExampleContract } from '@aztec/noir-contracts.js/DocsExample'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts index 619f24c3a7d..f586644a680 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts @@ -1,5 +1,5 @@ import { AztecAddress, CompleteAddress } from '@aztec/aztec.js'; -import { TokenContract, type Transfer } from '@aztec/noir-contracts.js'; +import { TokenContract, type Transfer } from '@aztec/noir-contracts.js/Token'; import { TokenContractTest } from './token_contract_test.js'; diff --git a/yarn-project/end-to-end/src/fixtures/token_utils.ts b/yarn-project/end-to-end/src/fixtures/token_utils.ts index ab573d3bbc5..503db51c48b 100644 --- a/yarn-project/end-to-end/src/fixtures/token_utils.ts +++ b/yarn-project/end-to-end/src/fixtures/token_utils.ts @@ -1,6 +1,6 @@ // docs:start:token_utils import { type AztecAddress, type Logger, type Wallet } from '@aztec/aztec.js'; -import { TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; export async function deployToken(adminWallet: Wallet, initialAdminBalance: bigint, logger: Logger) { logger.info(`Deploying Token contract...`); diff --git a/yarn-project/end-to-end/src/flakey_e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/flakey_e2e_inclusion_proofs_contract.test.ts index 534300c633a..7fc07cea262 100644 --- a/yarn-project/end-to-end/src/flakey_e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/flakey_e2e_inclusion_proofs_contract.test.ts @@ -9,8 +9,8 @@ import { } from '@aztec/aztec.js'; import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment'; import { randomInt } from '@aztec/foundation/crypto'; -import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; import { InclusionProofsContract } from '@aztec/noir-contracts.js/InclusionProofs'; +import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulTest'; import { jest } from '@jest/globals'; import { type MemDown, default as memdown } from 'memdown'; diff --git a/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts b/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts index 25755714a88..7e70f26a566 100644 --- a/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts +++ b/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts @@ -15,7 +15,7 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { times } from '@aztec/foundation/collection'; import { Secp256k1Signer, keccak256, randomBigInt, randomInt } from '@aztec/foundation/crypto'; import { ProofCommitmentEscrowAbi, RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; -import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; +import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulTest'; import { createPXEService, getPXEServiceConfig } from '@aztec/pxe'; import { diff --git a/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts b/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts index 984260e425c..d78898c8bd8 100644 --- a/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts +++ b/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts @@ -1,6 +1,6 @@ import { createAccounts } from '@aztec/accounts/testing'; import { Fr, type Logger, type PXE } from '@aztec/aztec.js'; -import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js'; +import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken'; import { foundry, sepolia } from 'viem/chains'; diff --git a/yarn-project/end-to-end/src/sample-dapp/deploy.mjs b/yarn-project/end-to-end/src/sample-dapp/deploy.mjs index 43217454be6..841ea5f7474 100644 --- a/yarn-project/end-to-end/src/sample-dapp/deploy.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/deploy.mjs @@ -2,7 +2,8 @@ import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; import { Contract, createPXEClient, loadContractArtifact, waitForPXE } from '@aztec/aztec.js'; // docs:end:deploy-imports -import { TokenContract, TokenContractArtifact } from '@aztec/noir-contracts.js'; +import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { writeFileSync } from 'fs'; import { fileURLToPath } from 'url'; diff --git a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts index 0d54c623f2c..0f1e1ace4ad 100644 --- a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts @@ -9,7 +9,7 @@ import { type PXE, type Wallet, } from '@aztec/aztec.js'; -import { FeeJuiceContract } from '@aztec/noir-contracts.js'; +import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { type Account, type Chain, type HttpTransport, type PublicClient, type WalletClient } from 'viem'; diff --git a/yarn-project/end-to-end/src/spartan/4epochs.test.ts b/yarn-project/end-to-end/src/spartan/4epochs.test.ts index 43d2a0da7ba..1ba5be9d3ab 100644 --- a/yarn-project/end-to-end/src/spartan/4epochs.test.ts +++ b/yarn-project/end-to-end/src/spartan/4epochs.test.ts @@ -1,7 +1,7 @@ import { EthCheatCodes, readFieldCompressedString } from '@aztec/aztec.js'; import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { createLogger } from '@aztec/foundation/log'; -import { TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index bb4528707a9..ae3abd9b625 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -1,7 +1,7 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type AccountWalletWithSecretKey, type AztecAddress, type PXE, createCompatibleClient } from '@aztec/aztec.js'; import { type Logger } from '@aztec/foundation/log'; -import { TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { addAccounts } from '../fixtures/snapshot_manager.js'; diff --git a/yarn-project/end-to-end/src/spartan/transfer.test.ts b/yarn-project/end-to-end/src/spartan/transfer.test.ts index 4f0d79215f7..28905e96afb 100644 --- a/yarn-project/end-to-end/src/spartan/transfer.test.ts +++ b/yarn-project/end-to-end/src/spartan/transfer.test.ts @@ -1,6 +1,6 @@ import { readFieldCompressedString } from '@aztec/aztec.js'; import { createLogger } from '@aztec/foundation/log'; -import { TokenContract } from '@aztec/noir-contracts.js'; +import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; diff --git a/yarn-project/noir-contracts.js/package.json b/yarn-project/noir-contracts.js/package.json index c54eb0a5965..3eed2ad6ae2 100644 --- a/yarn-project/noir-contracts.js/package.json +++ b/yarn-project/noir-contracts.js/package.json @@ -81,4 +81,4 @@ "engines": { "node": ">=18" } -} +} \ No newline at end of file diff --git a/yarn-project/noir-contracts.js/scripts/generate-types.sh b/yarn-project/noir-contracts.js/scripts/generate-types.sh index 26a7eb20560..fb380b01264 100755 --- a/yarn-project/noir-contracts.js/scripts/generate-types.sh +++ b/yarn-project/noir-contracts.js/scripts/generate-types.sh @@ -40,7 +40,10 @@ done node --no-warnings ../builder/dest/bin/cli.js codegen -o $OUT_DIR artifacts # Append exports for each generated TypeScript file to index.ts +echo "/** List of contract names exported by this package. */" >>"$INDEX" +echo "export const ContractNames = [" >>"$INDEX" find "$OUT_DIR" -maxdepth 1 -type f -name '*.ts' ! -name 'index.ts' | while read -r TS_FILE; do CONTRACT_NAME=$(basename "$TS_FILE" .ts) # Remove the .ts extension to get the contract name - echo "export * from './${CONTRACT_NAME}.js';" >>"$INDEX" + echo " '$CONTRACT_NAME'," >>"$INDEX" done +echo "];" >>"$INDEX" diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts index eec83ad034f..68a7c4f8427 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts @@ -2,7 +2,7 @@ import { type Tx, mockTx } from '@aztec/circuit-types'; import { AztecAddress, Fr, FunctionSelector, GasFees, GasSettings, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js'; import { poseidon2Hash } from '@aztec/foundation/crypto'; import { type Writeable } from '@aztec/foundation/types'; -import { FeeJuiceContract } from '@aztec/noir-contracts.js'; +import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { type MockProxy, mock, mockFn } from 'jest-mock-extended'; diff --git a/yarn-project/simulator/src/avm/fixtures/index.ts b/yarn-project/simulator/src/avm/fixtures/index.ts index 53a259419a8..fce5fb7eff4 100644 --- a/yarn-project/simulator/src/avm/fixtures/index.ts +++ b/yarn-project/simulator/src/avm/fixtures/index.ts @@ -4,7 +4,7 @@ import { type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; -import { AvmTestContractArtifact } from '@aztec/noir-contracts.js'; +import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; import { strict as assert } from 'assert'; import { mock } from 'jest-mock-extended'; diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 19d96f8b043..cc38e3ad1fa 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -55,14 +55,12 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; import { type FieldsOf } from '@aztec/foundation/types'; import { openTmpStore } from '@aztec/kv-store/lmdb'; import { type AppendOnlyTree, Poseidon, StandardTree, newTree } from '@aztec/merkle-tree'; -import { - ChildContractArtifact, - ImportTestContractArtifact, - ParentContractArtifact, - PendingNoteHashesContractArtifact, - StatefulTestContractArtifact, - TestContractArtifact, -} from '@aztec/noir-contracts.js'; +import { ChildContractArtifact } from '@aztec/noir-contracts.js/Child'; +import { ImportTestContractArtifact } from '@aztec/noir-contracts.js/ImportTest'; +import { ParentContractArtifact } from '@aztec/noir-contracts.js/Parent'; +import { PendingNoteHashesContractArtifact } from '@aztec/noir-contracts.js/PendingNoteHashes'; +import { StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulTest'; +import { TestContractArtifact } from '@aztec/noir-contracts.js/Test'; import { jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index 30cb6d85b6d..bf67b300f06 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -3,7 +3,7 @@ import { KeyValidationRequest, computeAppNullifierSecretKey, deriveKeys } from ' import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, type Point } from '@aztec/foundation/fields'; -import { TokenBlacklistContractArtifact } from '@aztec/noir-contracts.js'; +import { TokenBlacklistContractArtifact } from '@aztec/noir-contracts.js/TokenBlacklist'; import { type MockProxy, mock } from 'jest-mock-extended'; diff --git a/yarn-project/simulator/src/public/fixtures/index.ts b/yarn-project/simulator/src/public/fixtures/index.ts index bf4940feb8e..691c0f4ae85 100644 --- a/yarn-project/simulator/src/public/fixtures/index.ts +++ b/yarn-project/simulator/src/public/fixtures/index.ts @@ -29,7 +29,7 @@ import { type ContractArtifact, type FunctionArtifact } from '@aztec/foundation/ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { openTmpStore } from '@aztec/kv-store/lmdb'; -import { AvmTestContractArtifact } from '@aztec/noir-contracts.js'; +import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; import { PublicTxSimulator, WorldStateDB } from '@aztec/simulator'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { MerkleTrees } from '@aztec/world-state'; From b086c52110e5bc79a3d8eccbc2bc50cd68b3dc9b Mon Sep 17 00:00:00 2001 From: just-mitch <68168980+just-mitch@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:49:13 -0500 Subject: [PATCH 09/10] feat: json output for get_node_info (#10771) Need this to (easily) update the network joiner script. --- .../cli/src/cmds/pxe/get_node_info.ts | 79 +++++++++++++------ yarn-project/cli/src/cmds/pxe/index.ts | 3 +- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/yarn-project/cli/src/cmds/pxe/get_node_info.ts b/yarn-project/cli/src/cmds/pxe/get_node_info.ts index d2972b9f1e6..329b2428297 100644 --- a/yarn-project/cli/src/cmds/pxe/get_node_info.ts +++ b/yarn-project/cli/src/cmds/pxe/get_node_info.ts @@ -1,7 +1,14 @@ import { type AztecNode, type PXE, createAztecNodeClient, createCompatibleClient } from '@aztec/aztec.js'; import { type LogFn, type Logger } from '@aztec/foundation/log'; -export async function getNodeInfo(rpcUrl: string, pxeRequest: boolean, debugLogger: Logger, log: LogFn) { +export async function getNodeInfo( + rpcUrl: string, + pxeRequest: boolean, + debugLogger: Logger, + json: boolean, + log: LogFn, + logJson: (output: any) => void, +) { let client: AztecNode | PXE; if (pxeRequest) { client = await createCompatibleClient(rpcUrl, debugLogger); @@ -9,26 +16,54 @@ export async function getNodeInfo(rpcUrl: string, pxeRequest: boolean, debugLogg client = createAztecNodeClient(rpcUrl); } const info = await client.getNodeInfo(); - log(`Node Version: ${info.nodeVersion}`); - log(`Chain Id: ${info.l1ChainId}`); - log(`Protocol Version: ${info.protocolVersion}`); - log(`Node ENR: ${info.enr}`); - log(`L1 Contract Addresses:`); - log(` Rollup Address: ${info.l1ContractAddresses.rollupAddress.toString()}`); - log(` Registry Address: ${info.l1ContractAddresses.registryAddress.toString()}`); - log(` L1 -> L2 Inbox Address: ${info.l1ContractAddresses.inboxAddress.toString()}`); - log(` L2 -> L1 Outbox Address: ${info.l1ContractAddresses.outboxAddress.toString()}`); - log(` Fee Juice Address: ${info.l1ContractAddresses.feeJuiceAddress.toString()}`); - log(` Staking Asset Address: ${info.l1ContractAddresses.stakingAssetAddress.toString()}`); - log(` Fee Juice Portal Address: ${info.l1ContractAddresses.feeJuicePortalAddress.toString()}`); - log(` CoinIssuer Address: ${info.l1ContractAddresses.coinIssuerAddress.toString()}`); - log(` RewardDistributor Address: ${info.l1ContractAddresses.rewardDistributorAddress.toString()}`); - log(` GovernanceProposer Address: ${info.l1ContractAddresses.governanceProposerAddress.toString()}`); - log(` Governance Address: ${info.l1ContractAddresses.governanceAddress.toString()}`); + if (json) { + logJson({ + nodeVersion: info.nodeVersion, + l1ChainId: info.l1ChainId, + protocolVersion: info.protocolVersion, + enr: info.enr, + l1ContractAddresses: { + rollup: info.l1ContractAddresses.rollupAddress.toString(), + registry: info.l1ContractAddresses.registryAddress.toString(), + inbox: info.l1ContractAddresses.inboxAddress.toString(), + outbox: info.l1ContractAddresses.outboxAddress.toString(), + feeJuice: info.l1ContractAddresses.feeJuiceAddress.toString(), + stakingAsset: info.l1ContractAddresses.stakingAssetAddress.toString(), + feeJuicePortal: info.l1ContractAddresses.feeJuicePortalAddress.toString(), + coinIssuer: info.l1ContractAddresses.coinIssuerAddress.toString(), + rewardDistributor: info.l1ContractAddresses.rewardDistributorAddress.toString(), + governanceProposer: info.l1ContractAddresses.governanceProposerAddress.toString(), + governance: info.l1ContractAddresses.governanceAddress.toString(), + }, + protocolContractAddresses: { + classRegisterer: info.protocolContractAddresses.classRegisterer.toString(), + feeJuice: info.protocolContractAddresses.feeJuice.toString(), + instanceDeployer: info.protocolContractAddresses.instanceDeployer.toString(), + multiCallEntrypoint: info.protocolContractAddresses.multiCallEntrypoint.toString(), + }, + }); + } else { + log(`Node Version: ${info.nodeVersion}`); + log(`Chain Id: ${info.l1ChainId}`); + log(`Protocol Version: ${info.protocolVersion}`); + log(`Node ENR: ${info.enr}`); + log(`L1 Contract Addresses:`); + log(` Rollup Address: ${info.l1ContractAddresses.rollupAddress.toString()}`); + log(` Registry Address: ${info.l1ContractAddresses.registryAddress.toString()}`); + log(` L1 -> L2 Inbox Address: ${info.l1ContractAddresses.inboxAddress.toString()}`); + log(` L2 -> L1 Outbox Address: ${info.l1ContractAddresses.outboxAddress.toString()}`); + log(` Fee Juice Address: ${info.l1ContractAddresses.feeJuiceAddress.toString()}`); + log(` Staking Asset Address: ${info.l1ContractAddresses.stakingAssetAddress.toString()}`); + log(` Fee Juice Portal Address: ${info.l1ContractAddresses.feeJuicePortalAddress.toString()}`); + log(` CoinIssuer Address: ${info.l1ContractAddresses.coinIssuerAddress.toString()}`); + log(` RewardDistributor Address: ${info.l1ContractAddresses.rewardDistributorAddress.toString()}`); + log(` GovernanceProposer Address: ${info.l1ContractAddresses.governanceProposerAddress.toString()}`); + log(` Governance Address: ${info.l1ContractAddresses.governanceAddress.toString()}`); - log(`L2 Contract Addresses:`); - log(` Class Registerer: ${info.protocolContractAddresses.classRegisterer.toString()}`); - log(` Fee Juice: ${info.protocolContractAddresses.feeJuice.toString()}`); - log(` Instance Deployer: ${info.protocolContractAddresses.instanceDeployer.toString()}`); - log(` MultiCall: ${info.protocolContractAddresses.multiCallEntrypoint.toString()}`); + log(`L2 Contract Addresses:`); + log(` Class Registerer: ${info.protocolContractAddresses.classRegisterer.toString()}`); + log(` Fee Juice: ${info.protocolContractAddresses.feeJuice.toString()}`); + log(` Instance Deployer: ${info.protocolContractAddresses.instanceDeployer.toString()}`); + log(` MultiCall: ${info.protocolContractAddresses.multiCallEntrypoint.toString()}`); + } } diff --git a/yarn-project/cli/src/cmds/pxe/index.ts b/yarn-project/cli/src/cmds/pxe/index.ts index dc8a7a4bb6a..56fe779ac17 100644 --- a/yarn-project/cli/src/cmds/pxe/index.ts +++ b/yarn-project/cli/src/cmds/pxe/index.ts @@ -144,6 +144,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .command('get-node-info') .description('Gets the information of an Aztec node from a PXE or directly from an Aztec node.') .option('--node-url ', 'URL of the node.') + .option('--json', 'Emit output as json') .addOption(makePxeOption(false)) .action(async options => { const { getNodeInfo } = await import('./get_node_info.js'); @@ -153,7 +154,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger } else { url = options.rpcUrl; } - await getNodeInfo(url, !options.nodeUrl, debugLogger, log); + await getNodeInfo(url, !options.nodeUrl, debugLogger, options.json, log, logJson(log)); }); program From b0096ddc0e566ad27c713f43006068f3b5916d97 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Mon, 16 Dec 2024 14:23:59 -0300 Subject: [PATCH 10/10] chore: Split up protocol contract artifacts --- .../archiver/src/archiver/archiver.ts | 4 +-- yarn-project/archiver/src/factory.ts | 3 +- .../fee_juice_payment_method_with_claim.ts | 5 +-- yarn-project/aztec/src/bin/index.ts | 2 +- yarn-project/protocol-contracts/package.json | 3 +- .../src/auth-registry/index.ts | 16 +++++++-- .../protocol-contracts/src/bundle/index.ts | 36 +++++++++++++++++++ .../src/class-registerer/index.ts | 21 +++++++++-- .../protocol-contracts/src/fee-juice/index.ts | 18 ++++++++-- yarn-project/protocol-contracts/src/index.ts | 5 --- .../src/instance-deployer/index.ts | 20 +++++++++-- .../src/make_protocol_contract.ts | 23 ++++++++++++ .../src/multi-call-entrypoint/index.ts | 19 ++++++++-- .../src/protocol_contract.ts | 25 +------------ .../protocol-contracts/src/router/index.ts | 18 ++++++++-- .../src/scripts/generate_data.ts | 25 ------------- .../pxe/src/pxe_service/pxe_service.ts | 7 ++-- .../simulator/src/public/fee_payment.ts | 5 +-- .../simulator/src/public/public_db_sources.ts | 3 +- .../simulator/src/public/public_processor.ts | 3 +- .../src/public/public_processor_metrics.ts | 2 +- .../txe/src/txe_service/txe_service.ts | 3 +- 22 files changed, 177 insertions(+), 89 deletions(-) create mode 100644 yarn-project/protocol-contracts/src/bundle/index.ts create mode 100644 yarn-project/protocol-contracts/src/make_protocol_contract.ts diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 85c362642b9..c72c03b815f 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -47,10 +47,10 @@ import { elapsed } from '@aztec/foundation/timer'; import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { ContractClassRegisteredEvent, - ContractInstanceDeployedEvent, PrivateFunctionBroadcastedEvent, UnconstrainedFunctionBroadcastedEvent, -} from '@aztec/protocol-contracts'; +} from '@aztec/protocol-contracts/class-registerer'; +import { ContractInstanceDeployedEvent } from '@aztec/protocol-contracts/instance-deployer'; import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client'; import groupBy from 'lodash.groupby'; diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index afeb43cf7cb..817770dcd52 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -10,7 +10,8 @@ import { type DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb'; import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; import { TokenBridgeContractArtifact } from '@aztec/noir-contracts.js/TokenBridge'; -import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts'; +import { protocolContractNames } from '@aztec/protocol-contracts'; +import { getCanonicalProtocolContract } from '@aztec/protocol-contracts/bundle'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; diff --git a/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts b/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts index f69f515388e..4adcfcdddc7 100644 --- a/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts +++ b/yarn-project/aztec.js/src/fee/fee_juice_payment_method_with_claim.ts @@ -1,7 +1,8 @@ import { type FunctionCall } from '@aztec/circuit-types'; import { type AztecAddress, Fr, FunctionSelector } from '@aztec/circuits.js'; import { FunctionType } from '@aztec/foundation/abi'; -import { ProtocolContractAddress, ProtocolContractArtifact } from '@aztec/protocol-contracts'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice'; import { type L2AmountClaim } from '../utils/portal_manager.js'; import { FeeJuicePaymentMethod } from './fee_juice_payment_method.js'; @@ -23,7 +24,7 @@ export class FeeJuicePaymentMethodWithClaim extends FeeJuicePaymentMethod { */ override getFunctionCalls(): Promise { const selector = FunctionSelector.fromNameAndParameters( - ProtocolContractArtifact.FeeJuice.functions.find(f => f.name === 'claim')!, + getCanonicalFeeJuice().artifact.functions.find(f => f.name === 'claim')!, ); return Promise.resolve([ diff --git a/yarn-project/aztec/src/bin/index.ts b/yarn-project/aztec/src/bin/index.ts index 39426945fd5..ff4828e6752 100644 --- a/yarn-project/aztec/src/bin/index.ts +++ b/yarn-project/aztec/src/bin/index.ts @@ -1,5 +1,4 @@ #!/usr/bin/env node -import { fileURLToPath } from '@aztec/aztec.js'; import { injectCommands as injectBuilderCommands } from '@aztec/builder'; import { injectCommands as injectWalletCommands } from '@aztec/cli-wallet'; import { injectCommands as injectContractCommands } from '@aztec/cli/contracts'; @@ -9,6 +8,7 @@ import { injectCommands as injectL1Commands } from '@aztec/cli/l1'; import { injectCommands as injectMiscCommands } from '@aztec/cli/misc'; import { injectCommands as injectPXECommands } from '@aztec/cli/pxe'; import { createConsoleLogger, createLogger } from '@aztec/foundation/log'; +import { fileURLToPath } from '@aztec/foundation/url'; import { Command } from 'commander'; import { readFileSync } from 'fs'; diff --git a/yarn-project/protocol-contracts/package.json b/yarn-project/protocol-contracts/package.json index d12d967b95e..da2b4b987e4 100644 --- a/yarn-project/protocol-contracts/package.json +++ b/yarn-project/protocol-contracts/package.json @@ -6,6 +6,7 @@ "type": "module", "exports": { ".": "./dest/index.js", + "./bundle": "./dest/bundle/index.js", "./*": "./dest/*/index.js" }, "typedocOptions": { @@ -99,4 +100,4 @@ "engines": { "node": ">=18" } -} +} \ No newline at end of file diff --git a/yarn-project/protocol-contracts/src/auth-registry/index.ts b/yarn-project/protocol-contracts/src/auth-registry/index.ts index a5d5c399e0e..60438443618 100644 --- a/yarn-project/protocol-contracts/src/auth-registry/index.ts +++ b/yarn-project/protocol-contracts/src/auth-registry/index.ts @@ -1,6 +1,18 @@ -import { type ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { type NoirCompiledContract } from '@aztec/types/noir'; + +import AuthRegistryJson from '../../artifacts/AuthRegistry.json' assert { type: 'json' }; +import { makeProtocolContract } from '../make_protocol_contract.js'; +import { type ProtocolContract } from '../protocol_contract.js'; + +let protocolContract: ProtocolContract; + +export const AuthRegistryArtifact = loadContractArtifact(AuthRegistryJson as NoirCompiledContract); /** Returns the canonical deployment of the auth registry. */ export function getCanonicalAuthRegistry(): ProtocolContract { - return getCanonicalProtocolContract('AuthRegistry'); + if (!protocolContract) { + protocolContract = makeProtocolContract('AuthRegistry', AuthRegistryArtifact); + } + return protocolContract; } diff --git a/yarn-project/protocol-contracts/src/bundle/index.ts b/yarn-project/protocol-contracts/src/bundle/index.ts new file mode 100644 index 00000000000..a8203e7dc33 --- /dev/null +++ b/yarn-project/protocol-contracts/src/bundle/index.ts @@ -0,0 +1,36 @@ +import { getContractClassFromArtifact, getContractInstanceFromDeployParams } from '@aztec/circuits.js'; +import { type ContractArtifact } from '@aztec/foundation/abi'; + +import { AuthRegistryArtifact } from '../auth-registry/index.js'; +import { ContractClassRegistererArtifact } from '../class-registerer/index.js'; +import { FeeJuiceArtifact } from '../fee-juice/index.js'; +import { ContractInstanceDeployerArtifact } from '../instance-deployer/index.js'; +import { MultiCallEntrypointArtifact } from '../multi-call-entrypoint/index.js'; +import { type ProtocolContract } from '../protocol_contract.js'; +import { ProtocolContractAddress, type ProtocolContractName, ProtocolContractSalt } from '../protocol_contract_data.js'; +import { RouterArtifact } from '../router/index.js'; + +/** Returns the canonical deployment a given artifact. */ +export function getCanonicalProtocolContract(name: ProtocolContractName): ProtocolContract { + const artifact = ProtocolContractArtifact[name]; + const address = ProtocolContractAddress[name]; + const salt = ProtocolContractSalt[name]; + // TODO(@spalladino): This computes the contract class from the artifact twice. + const contractClass = getContractClassFromArtifact(artifact); + const instance = getContractInstanceFromDeployParams(artifact, { salt }); + return { + instance: { ...instance, address }, + contractClass, + artifact, + address, + }; +} + +export const ProtocolContractArtifact: Record = { + AuthRegistry: AuthRegistryArtifact, + ContractInstanceDeployer: ContractInstanceDeployerArtifact, + ContractClassRegisterer: ContractClassRegistererArtifact, + MultiCallEntrypoint: MultiCallEntrypointArtifact, + FeeJuice: FeeJuiceArtifact, + Router: RouterArtifact, +}; diff --git a/yarn-project/protocol-contracts/src/class-registerer/index.ts b/yarn-project/protocol-contracts/src/class-registerer/index.ts index b30844b28fa..9967cc1a225 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/index.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/index.ts @@ -1,10 +1,25 @@ -import { type ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { type NoirCompiledContract } from '@aztec/types/noir'; + +import ContractClassRegistererJson from '../../artifacts/ContractClassRegisterer.json' assert { type: 'json' }; +import { makeProtocolContract } from '../make_protocol_contract.js'; +import { type ProtocolContract } from '../protocol_contract.js'; export * from './contract_class_registered_event.js'; export * from './private_function_broadcasted_event.js'; export * from './unconstrained_function_broadcasted_event.js'; -/** Returns the canonical deployment of the class registerer contract. */ +export const ContractClassRegistererArtifact = loadContractArtifact( + ContractClassRegistererJson as NoirCompiledContract, +); + +let protocolContract: ProtocolContract; + +/** Returns the canonical deployment of the contract. */ export function getCanonicalClassRegisterer(): ProtocolContract { - return getCanonicalProtocolContract('ContractClassRegisterer'); + if (!protocolContract) { + const artifact = ContractClassRegistererArtifact; + protocolContract = makeProtocolContract('ContractClassRegisterer', artifact); + } + return protocolContract; } diff --git a/yarn-project/protocol-contracts/src/fee-juice/index.ts b/yarn-project/protocol-contracts/src/fee-juice/index.ts index b92451bdb74..3c2baabf873 100644 --- a/yarn-project/protocol-contracts/src/fee-juice/index.ts +++ b/yarn-project/protocol-contracts/src/fee-juice/index.ts @@ -1,6 +1,18 @@ -import { type ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { type NoirCompiledContract } from '@aztec/types/noir'; -/** Returns the canonical deployment of the Fee Juice. */ +import FeeJuiceJson from '../../artifacts/FeeJuice.json' assert { type: 'json' }; +import { makeProtocolContract } from '../make_protocol_contract.js'; +import { type ProtocolContract } from '../protocol_contract.js'; + +export const FeeJuiceArtifact = loadContractArtifact(FeeJuiceJson as NoirCompiledContract); + +let protocolContract: ProtocolContract; + +/** Returns the canonical deployment of the contract. */ export function getCanonicalFeeJuice(): ProtocolContract { - return getCanonicalProtocolContract('FeeJuice'); + if (!protocolContract) { + protocolContract = makeProtocolContract('FeeJuice', FeeJuiceArtifact); + } + return protocolContract; } diff --git a/yarn-project/protocol-contracts/src/index.ts b/yarn-project/protocol-contracts/src/index.ts index 029032c827f..adb7745ebef 100644 --- a/yarn-project/protocol-contracts/src/index.ts +++ b/yarn-project/protocol-contracts/src/index.ts @@ -1,8 +1,3 @@ -export * from './auth-registry/index.js'; -export * from './class-registerer/index.js'; -export * from './fee-juice/index.js'; -export * from './instance-deployer/index.js'; -export * from './multi-call-entrypoint/index.js'; export * from './protocol_contract.js'; export * from './protocol_contract_data.js'; export * from './protocol_contract_tree.js'; diff --git a/yarn-project/protocol-contracts/src/instance-deployer/index.ts b/yarn-project/protocol-contracts/src/instance-deployer/index.ts index 1253aeb915d..7af519ff744 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/index.ts +++ b/yarn-project/protocol-contracts/src/instance-deployer/index.ts @@ -1,8 +1,22 @@ -import { type ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { type NoirCompiledContract } from '@aztec/types/noir'; + +import ContractInstanceDeployerJson from '../../artifacts/ContractInstanceDeployer.json' assert { type: 'json' }; +import { makeProtocolContract } from '../make_protocol_contract.js'; +import { type ProtocolContract } from '../protocol_contract.js'; export * from './contract_instance_deployed_event.js'; -/** Returns the canonical deployment of the instance deployer contract. */ +export const ContractInstanceDeployerArtifact = loadContractArtifact( + ContractInstanceDeployerJson as NoirCompiledContract, +); + +let protocolContract: ProtocolContract; + +/** Returns the canonical deployment of the contract. */ export function getCanonicalInstanceDeployer(): ProtocolContract { - return getCanonicalProtocolContract('ContractInstanceDeployer'); + if (!protocolContract) { + protocolContract = makeProtocolContract('ContractInstanceDeployer', ContractInstanceDeployerArtifact); + } + return protocolContract; } diff --git a/yarn-project/protocol-contracts/src/make_protocol_contract.ts b/yarn-project/protocol-contracts/src/make_protocol_contract.ts new file mode 100644 index 00000000000..fdd05e4e4c5 --- /dev/null +++ b/yarn-project/protocol-contracts/src/make_protocol_contract.ts @@ -0,0 +1,23 @@ +import { getContractClassFromArtifact, getContractInstanceFromDeployParams } from '@aztec/circuits.js'; +import { type ContractArtifact } from '@aztec/foundation/abi'; + +import { type ProtocolContract } from './protocol_contract.js'; +import { ProtocolContractAddress, type ProtocolContractName, ProtocolContractSalt } from './protocol_contract_data.js'; + +/** + * Returns the canonical deployment given its name and artifact. + * To be used internally within the protocol-contracts package. + */ +export function makeProtocolContract(name: ProtocolContractName, artifact: ContractArtifact): ProtocolContract { + const address = ProtocolContractAddress[name]; + const salt = ProtocolContractSalt[name]; + // TODO(@spalladino): This computes the contract class from the artifact twice. + const contractClass = getContractClassFromArtifact(artifact); + const instance = getContractInstanceFromDeployParams(artifact, { salt }); + return { + instance: { ...instance, address }, + contractClass, + artifact, + address, + }; +} diff --git a/yarn-project/protocol-contracts/src/multi-call-entrypoint/index.ts b/yarn-project/protocol-contracts/src/multi-call-entrypoint/index.ts index 7766894654c..862a32b76a2 100644 --- a/yarn-project/protocol-contracts/src/multi-call-entrypoint/index.ts +++ b/yarn-project/protocol-contracts/src/multi-call-entrypoint/index.ts @@ -1,5 +1,18 @@ -import { type ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { type NoirCompiledContract } from '@aztec/types/noir'; -export function getCanonicalMultiCallEntrypointContract(): ProtocolContract { - return getCanonicalProtocolContract('MultiCallEntrypoint'); +import MultiCallEntrypointJson from '../../artifacts/MultiCallEntrypoint.json' assert { type: 'json' }; +import { makeProtocolContract } from '../make_protocol_contract.js'; +import { type ProtocolContract } from '../protocol_contract.js'; + +export const MultiCallEntrypointArtifact = loadContractArtifact(MultiCallEntrypointJson as NoirCompiledContract); + +let protocolContract: ProtocolContract; + +/** Returns the canonical deployment of the contract. */ +export function getCanonicalMultiCallEntrypoint(): ProtocolContract { + if (!protocolContract) { + protocolContract = makeProtocolContract('MultiCallEntrypoint', MultiCallEntrypointArtifact); + } + return protocolContract; } diff --git a/yarn-project/protocol-contracts/src/protocol_contract.ts b/yarn-project/protocol-contracts/src/protocol_contract.ts index bf3f2dbd805..96e8d69e987 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract.ts @@ -3,17 +3,10 @@ import { type ContractClassIdPreimage, type ContractClassWithId, type ContractInstanceWithAddress, - getContractClassFromArtifact, - getContractInstanceFromDeployParams, } from '@aztec/circuits.js'; import { type ContractArtifact } from '@aztec/foundation/abi'; -import { - ProtocolContractAddress, - ProtocolContractArtifact, - type ProtocolContractName, - ProtocolContractSalt, -} from './protocol_contract_data.js'; +import { ProtocolContractAddress } from './protocol_contract_data.js'; /** Represents a canonical contract in the protocol. */ export interface ProtocolContract { @@ -27,22 +20,6 @@ export interface ProtocolContract { address: AztecAddress; } -/** Returns the canonical deployment a given artifact. */ -export function getCanonicalProtocolContract(name: ProtocolContractName): ProtocolContract { - const artifact = ProtocolContractArtifact[name]; - const address = ProtocolContractAddress[name]; - const salt = ProtocolContractSalt[name]; - // TODO(@spalladino): This computes the contract class from the artifact twice. - const contractClass = getContractClassFromArtifact(artifact); - const instance = getContractInstanceFromDeployParams(artifact, { salt }); - return { - instance: { ...instance, address }, - contractClass, - artifact, - address, - }; -} - export function isProtocolContract(address: AztecAddress) { return Object.values(ProtocolContractAddress).some(a => a.equals(address)); } diff --git a/yarn-project/protocol-contracts/src/router/index.ts b/yarn-project/protocol-contracts/src/router/index.ts index 8ea8e67e12a..a8bf704aa68 100644 --- a/yarn-project/protocol-contracts/src/router/index.ts +++ b/yarn-project/protocol-contracts/src/router/index.ts @@ -1,6 +1,18 @@ -import { type ProtocolContract, getCanonicalProtocolContract } from '../protocol_contract.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { type NoirCompiledContract } from '@aztec/types/noir'; -/** Returns the canonical deployment of the router. */ +import RouterJson from '../../artifacts/Router.json' assert { type: 'json' }; +import { makeProtocolContract } from '../make_protocol_contract.js'; +import { type ProtocolContract } from '../protocol_contract.js'; + +export const RouterArtifact = loadContractArtifact(RouterJson as NoirCompiledContract); + +let protocolContract: ProtocolContract; + +/** Returns the canonical deployment of the contract. */ export function getCanonicalRouter(): ProtocolContract { - return getCanonicalProtocolContract('Router'); + if (!protocolContract) { + protocolContract = makeProtocolContract('Router', RouterArtifact); + } + return protocolContract; } diff --git a/yarn-project/protocol-contracts/src/scripts/generate_data.ts b/yarn-project/protocol-contracts/src/scripts/generate_data.ts index edc9608d7cc..b803b63f8f0 100644 --- a/yarn-project/protocol-contracts/src/scripts/generate_data.ts +++ b/yarn-project/protocol-contracts/src/scripts/generate_data.ts @@ -97,26 +97,6 @@ function generateNames(names: string[]) { `; } -function generateArtifacts(names: string[]) { - const imports = names - .map(name => { - return ` - import ${name}Json from '../artifacts/${name}.json' assert { type: 'json' }; - `; - }) - .join('\n'); - - const exports = names.map(name => `${name}: loadContractArtifact(${name}Json as NoirCompiledContract)`).join(',\n'); - - return ` - ${imports} - - export const ProtocolContractArtifact: Record = { - ${exports} - }; - `; -} - function generateSalts(names: string[]) { return ` export const ProtocolContractSalt: Record = { @@ -165,14 +145,9 @@ async function generateOutputFile(names: string[], leaves: Fr[]) { const content = ` // GENERATED FILE - DO NOT EDIT. RUN \`yarn generate\` or \`yarn generate:data\` import { AztecAddress, Fr } from '@aztec/circuits.js'; - import { type ContractArtifact } from '@aztec/foundation/abi'; - import { loadContractArtifact } from '@aztec/types/abi'; - import { type NoirCompiledContract } from '@aztec/types/noir'; ${generateNames(names)} - ${generateArtifacts(names)} - ${generateSalts(names)} ${generateContractAddresses(names)} diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index f2b1f419909..86cc9af654e 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -58,11 +58,8 @@ import { Fr, type Point } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { type KeyStore } from '@aztec/key-store'; import { type L2TipsStore } from '@aztec/kv-store/stores'; -import { - ProtocolContractAddress, - getCanonicalProtocolContract, - protocolContractNames, -} from '@aztec/protocol-contracts'; +import { ProtocolContractAddress, protocolContractNames } from '@aztec/protocol-contracts'; +import { getCanonicalProtocolContract } from '@aztec/protocol-contracts/bundle'; import { type AcirSimulator } from '@aztec/simulator/client'; import { inspect } from 'util'; diff --git a/yarn-project/simulator/src/public/fee_payment.ts b/yarn-project/simulator/src/public/fee_payment.ts index 43cf1836afd..e8879ffae47 100644 --- a/yarn-project/simulator/src/public/fee_payment.ts +++ b/yarn-project/simulator/src/public/fee_payment.ts @@ -1,13 +1,14 @@ import { computePublicDataTreeLeafSlot, deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; -import { ProtocolContractAddress, ProtocolContractArtifact } from '@aztec/protocol-contracts'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice'; /** * Computes the storage slot within the Fee Juice contract for the balance of the fee payer. */ export function computeFeePayerBalanceStorageSlot(feePayer: AztecAddress) { - return deriveStorageSlotInMap(ProtocolContractArtifact.FeeJuice.storageLayout.balances.slot, feePayer); + return deriveStorageSlotInMap(FeeJuiceArtifact.storageLayout.balances.slot, feePayer); } /** diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index 5e0584a7a1b..f9c64fe38f7 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -22,7 +22,8 @@ import { import { computeL1ToL2MessageNullifier, computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { createLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; -import { ContractClassRegisteredEvent, ContractInstanceDeployedEvent } from '@aztec/protocol-contracts'; +import { ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer'; +import { ContractInstanceDeployedEvent } from '@aztec/protocol-contracts/instance-deployer'; import { type CommitmentsDB, MessageLoadOracleInputs, diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index b42e1d09e09..bfc52e17219 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -24,7 +24,8 @@ import { import { padArrayEnd } from '@aztec/foundation/collection'; import { createLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; -import { ContractClassRegisteredEvent, ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer'; import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client'; import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from './fee_payment.js'; diff --git a/yarn-project/simulator/src/public/public_processor_metrics.ts b/yarn-project/simulator/src/public/public_processor_metrics.ts index ccc1ee9daad..84d9b52cbde 100644 --- a/yarn-project/simulator/src/public/public_processor_metrics.ts +++ b/yarn-project/simulator/src/public/public_processor_metrics.ts @@ -1,5 +1,5 @@ import { type TxExecutionPhase } from '@aztec/circuit-types'; -import { type ContractClassRegisteredEvent } from '@aztec/protocol-contracts'; +import { type ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer'; import { Attributes, type Histogram, diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 9d87db3c1b4..f372c0ac5ec 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -15,7 +15,8 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { type Logger } from '@aztec/foundation/log'; import { KeyStore } from '@aztec/key-store'; import { openTmpStore } from '@aztec/kv-store/lmdb'; -import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/protocol-contracts'; +import { protocolContractNames } from '@aztec/protocol-contracts'; +import { getCanonicalProtocolContract } from '@aztec/protocol-contracts/bundle'; import { enrichPublicSimulationError } from '@aztec/pxe'; import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';