diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp index 7be9d1e7ab3..b083fa57680 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp @@ -122,9 +122,11 @@ class AvmExecutionTests : public ::testing::Test { auto tagging_key = grumpkin::g1::affine_one; PublicKeysHint public_keys{ nullifier_key, incoming_viewing_key, outgoing_viewing_key, tagging_key }; ContractInstanceHint contract_instance = { - FF::one() /* temp address */, true /* exists */, FF(2) /* salt */, FF(3) /* deployer_addr */, class_id, + FF::one() /* temp address */, true /* exists */, FF(2) /* salt */, FF(3) /* deployer_addr */, class_id, class_id, FF(8) /* initialisation_hash */, public_keys, /*membership_hint=*/ { .low_leaf_preimage = { .nullifier = 0, .next_nullifier = 0, .next_index = 0, }, .low_leaf_index = 0, .low_leaf_sibling_path = {} }, + /* update_hint*/ { .leaf_preimage = { .slot = 0, .value = 0, .next_index = 0, .next_slot = 0, }, .leaf_index = 0, .sibling_path = {} }, + /* update_preimage */ {}, }; FF address = AvmBytecodeTraceBuilder::compute_address_from_instance(contract_instance); contract_instance.address = address; @@ -2293,10 +2295,13 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcode) .exists = true, .salt = 2, .deployer_addr = 42, - .contract_class_id = 66, + .current_contract_class_id = 66, + .original_contract_class_id = 66, .initialisation_hash = 99, .public_keys = public_keys_hints, - .membership_hint = { .low_leaf_preimage = { .nullifier = 0, .next_nullifier = 0, .next_index = 0, }, .low_leaf_index = 0, .low_leaf_sibling_path = {} }, + .initialization_membership_hint = { .low_leaf_preimage = { .nullifier = 0, .next_nullifier = 0, .next_index = 0, }, .low_leaf_index = 0, .low_leaf_sibling_path = {} }, + .update_membership_hint = { .leaf_preimage = { .slot = 0, .value = 0, .next_index = 0, .next_slot = 0, }, .leaf_index = 0, .sibling_path = {} }, + .update_preimage = {} }; auto execution_hints = ExecutionHints().with_contract_instance_hints({ { address, instance } }); @@ -2341,7 +2346,7 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcode) std::vector const calldata{}; // alternating member value, exists bool std::vector const expected_returndata = { - instance.deployer_addr, 1, instance.contract_class_id, 1, instance.initialisation_hash, 1, + instance.deployer_addr, 1, instance.current_contract_class_id, 1, instance.initialisation_hash, 1, }; std::vector returndata{}; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp index 36137f3c84a..47b4583f47b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp @@ -32,7 +32,7 @@ FF AvmBytecodeTraceBuilder::compute_address_from_instance(const ContractInstance contract_instance.initialisation_hash, contract_instance.deployer_addr }); FF partial_address = poseidon2::hash( - { GENERATOR_INDEX__PARTIAL_ADDRESS, contract_instance.contract_class_id, salted_initialization_hash }); + { GENERATOR_INDEX__PARTIAL_ADDRESS, contract_instance.original_contract_class_id, salted_initialization_hash }); std::vector public_keys_hash_fields = contract_instance.public_keys.to_fields(); std::vector public_key_hash_vec{ GENERATOR_INDEX__PUBLIC_KEYS_HASH }; @@ -144,7 +144,7 @@ void AvmBytecodeTraceBuilder::build_bytecode_hash_columns() contract_bytecode.contract_class_id_preimage.private_fn_root, running_hash); // Assert that the computed class id is the same as what we received as the hint - ASSERT(last_entry.class_id == contract_bytecode.contract_instance.contract_class_id); + ASSERT(last_entry.class_id == contract_bytecode.contract_instance.current_contract_class_id); last_entry.contract_address = compute_address_from_instance(contract_bytecode.contract_instance); // Assert that the computed contract address is the same as what we received as the hint diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp index 568af787cf0..16a270fee75 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp @@ -163,10 +163,13 @@ struct ContractInstanceHint { bool exists; // Useful for membership checks FF salt{}; FF deployer_addr{}; - FF contract_class_id{}; + FF current_contract_class_id{}; + FF original_contract_class_id{}; FF initialisation_hash{}; PublicKeysHint public_keys; - NullifierReadTreeHint membership_hint; + NullifierReadTreeHint initialization_membership_hint; + PublicDataReadTreeHint update_membership_hint; + std::vector update_preimage; }; inline void read(uint8_t const*& it, PublicKeysHint& hint) @@ -187,10 +190,13 @@ inline void read(uint8_t const*& it, ContractInstanceHint& hint) read(it, hint.exists); read(it, hint.salt); read(it, hint.deployer_addr); - read(it, hint.contract_class_id); + read(it, hint.current_contract_class_id); + read(it, hint.original_contract_class_id); read(it, hint.initialisation_hash); read(it, hint.public_keys); - read(it, hint.membership_hint); + read(it, hint.initialization_membership_hint); + read(it, hint.update_membership_hint); + read(it, hint.update_preimage); } struct AvmContractBytecode { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp index 9749ec073d3..b7e02fcfad4 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp @@ -171,22 +171,67 @@ std::vector AvmTraceBuilder::get_bytecode_from_hints(const FF contract_ // TODO: still need to make sure that the contract address does correspond to this class id const AvmContractBytecode bytecode_hint = *std::ranges::find_if(execution_hints.all_contract_bytecode, [contract_class_id](const auto& contract) { - return contract.contract_instance.contract_class_id == contract_class_id; + return contract.contract_instance.current_contract_class_id == contract_class_id; }); return bytecode_hint.bytecode; } +void AvmTraceBuilder::validate_contract_instance_current_class_id(uint32_t clk, const ContractInstanceHint& instance) +{ + if (is_canonical(instance.address)) { + return; + } + // First validate the update_preimage against the public data tree + PublicDataReadTreeHint read_hint = instance.update_membership_hint; + + const FF shared_mutable_slot = Poseidon2::hash({ UPDATED_CLASS_IDS_SLOT, instance.address }); + const FF hash_slot = Poseidon2::hash({ SHARED_MUTABLE_HASH_SEPARATOR, shared_mutable_slot }); + const FF hash_leaf_slot = + AvmMerkleTreeTraceBuilder::unconstrained_compute_public_tree_leaf_slot(DEPLOYER_CONTRACT_ADDRESS, hash_slot); + bool exists = read_hint.leaf_preimage.slot == hash_leaf_slot; + + bool is_member = merkle_tree_trace_builder.perform_storage_read( + clk, read_hint.leaf_preimage, read_hint.leaf_index, read_hint.sibling_path); + // membership check must always pass + ASSERT(is_member); + + if (exists) { + const FF reproduced_hash = Poseidon2::hash(instance.update_preimage); + ASSERT(reproduced_hash == read_hint.leaf_preimage.value); + } else { + AvmMerkleTreeTraceBuilder::assert_public_data_non_membership_check(read_hint.leaf_preimage, hash_leaf_slot); + // ensure instance.update_preimage is all zeroes + ASSERT(std::all_of( + instance.update_preimage.begin(), instance.update_preimage.end(), [](const auto& x) { return x == 0; })); + } + + // update_preimage is validated, now validate the contract class id + FF expected_current_class_id; + const FF prev_value = instance.update_preimage[0]; + const FF next_value = instance.update_preimage[1]; + const uint32_t block_of_change = static_cast(instance.update_preimage[2]); + + // Fourth item is related to update delays which we don't care. + if (static_cast(public_inputs.global_variables.block_number) < block_of_change) { + // original class id was validated against the address + expected_current_class_id = prev_value == 0 ? instance.original_contract_class_id : prev_value; + } else { + expected_current_class_id = next_value == 0 ? instance.original_contract_class_id : next_value; + } + ASSERT(expected_current_class_id == instance.current_contract_class_id); +} + std::vector AvmTraceBuilder::get_bytecode(const FF contract_address, bool check_membership) { auto clk = static_cast(main_trace.size()) + 1; ASSERT(execution_hints.contract_instance_hints.contains(contract_address)); const ContractInstanceHint instance_hint = execution_hints.contract_instance_hints.at(contract_address); - const FF contract_class_id = instance_hint.contract_class_id; + const FF contract_class_id = instance_hint.current_contract_class_id; bool exists = true; if (check_membership && !is_canonical(contract_address)) { - if (bytecode_membership_cache.find(contract_address) != bytecode_membership_cache.end()) { + if (contract_instance_membership_cache.find(contract_address) != contract_instance_membership_cache.end()) { // If we have already seen the contract address, we can skip the membership check and used the cached // membership proof vinfo("Found bytecode for contract address in cache: ", contract_address); @@ -195,7 +240,7 @@ std::vector AvmTraceBuilder::get_bytecode(const FF contract_address, bo const auto contract_address_nullifier = AvmMerkleTreeTraceBuilder::unconstrained_silo_nullifier( DEPLOYER_CONTRACT_ADDRESS, /*nullifier=*/contract_address); // nullifier read hint for the contract address - NullifierReadTreeHint nullifier_read_hint = instance_hint.membership_hint; + NullifierReadTreeHint nullifier_read_hint = instance_hint.initialization_membership_hint; // If the hinted preimage matches the contract address nullifier, the membership check will prove its existence, // otherwise the membership check will prove that a low-leaf exists that skips the contract address nullifier. @@ -212,7 +257,7 @@ std::vector AvmTraceBuilder::get_bytecode(const FF contract_address, bo // This was a membership proof! // Assert that the hint's exists flag matches. The flag isn't really necessary... ASSERT(instance_hint.exists); - bytecode_membership_cache.insert(contract_address); + contract_instance_membership_cache.insert(contract_address); // The cache contains all the unique contract class ids we have seen so far // If this bytecode retrievals have reached the number of unique contract class IDs, can't make @@ -223,7 +268,8 @@ std::vector AvmTraceBuilder::get_bytecode(const FF contract_address, bo MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS); throw std::runtime_error("Limit reached for contract calls to unique class id."); } - contract_class_id_cache.insert(instance_hint.contract_class_id); + contract_class_id_cache.insert(instance_hint.current_contract_class_id); + validate_contract_instance_current_class_id(clk, instance_hint); return get_bytecode_from_hints(contract_class_id); } else { // This was a non-membership proof! @@ -3507,12 +3553,13 @@ AvmError AvmTraceBuilder::op_get_contract_instance( // Read the contract instance hint ContractInstanceHint instance = execution_hints.contract_instance_hints.at(contract_address); - if (is_canonical(contract_address)) { - // skip membership check for canonical contracts + if (is_canonical(contract_address) || + (contract_instance_membership_cache.find(contract_address) != contract_instance_membership_cache.end())) { + // skip membership check for canonical contracts and contracts already verified exists = true; } else { // nullifier read hint for the contract address - NullifierReadTreeHint nullifier_read_hint = instance.membership_hint; + NullifierReadTreeHint nullifier_read_hint = instance.initialization_membership_hint; // If the hinted preimage matches the contract address nullifier, the membership check will prove its // existence, otherwise the membership check will prove that a low-leaf exists that skips the contract @@ -3539,6 +3586,9 @@ AvmError AvmTraceBuilder::op_get_contract_instance( (nullifier_read_hint.low_leaf_preimage.next_nullifier == FF::zero() || contract_address_nullifier > nullifier_read_hint.low_leaf_preimage.next_nullifier)); } + validate_contract_instance_current_class_id(clk, instance); + + contract_instance_membership_cache.insert(contract_address); } if (exists) { @@ -3547,7 +3597,7 @@ AvmError AvmTraceBuilder::op_get_contract_instance( member_value = instance.deployer_addr; break; case ContractInstanceMember::CLASS_ID: - member_value = instance.contract_class_id; + member_value = instance.current_contract_class_id; break; case ContractInstanceMember::INIT_HASH: member_value = instance.initialisation_hash; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp index ad5705dc120..0a7fddc827c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp @@ -233,9 +233,11 @@ class AvmTraceBuilder { void checkpoint_non_revertible_state(); void rollback_to_non_revertible_checkpoint(); std::vector get_bytecode(const FF contract_address, bool check_membership = false); + void validate_contract_instance_current_class_id(uint32_t clk, const ContractInstanceHint& instance); + // Used to track the unique class ids, could also be used to cache membership checks of class ids std::unordered_set contract_class_id_cache; - std::unordered_set bytecode_membership_cache; + std::unordered_set contract_instance_membership_cache; void insert_private_state(const std::vector& siloed_nullifiers, const std::vector& unique_note_hashes); void insert_private_revertible_state(const std::vector& siloed_nullifiers, const std::vector& siloed_note_hashes); diff --git a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp index 836a5fd2a2c..5d59afea159 100644 --- a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp @@ -27,6 +27,10 @@ #define FEE_JUICE_ADDRESS 5 #define ROUTER_ADDRESS 6 #define FEE_JUICE_BALANCES_SLOT 1 +#define UPDATED_CLASS_IDS_SLOT 1 +#define SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR 0 +#define SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR 1 +#define SHARED_MUTABLE_HASH_SEPARATOR 2 #define AZTEC_ADDRESS_LENGTH 1 #define GAS_FEES_LENGTH 2 #define GAS_LENGTH 2 diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp index b806ccb82b1..900bd95427c 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp @@ -29,14 +29,22 @@ struct ContractInstanceHint { bool exists; FF salt; AztecAddress deployer; - ContractClassId contractClassId; + ContractClassId currentContractClassId; + ContractClassId originalContractClassId; FF initializationHash; PublicKeysHint publicKeys; // TODO: missing membership hints. bool operator==(const ContractInstanceHint& other) const = default; - MSGPACK_FIELDS(address, exists, salt, deployer, contractClassId, initializationHash, publicKeys); + MSGPACK_FIELDS(address, + exists, + salt, + deployer, + currentContractClassId, + originalContractClassId, + initializationHash, + publicKeys); }; struct ContractClassHint { diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.test.cpp b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.test.cpp index c76cdece1f8..f4dc2f00f59 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.test.cpp @@ -52,7 +52,8 @@ TEST(AvmInputsTest, Deserialization) .exists = true, .salt = FF(0xdeadbeef), .deployer = AztecAddress(0x000010), - .contractClassId = ContractClassId(0x41181337), + .currentContractClassId = ContractClassId(0x41181337), + .originalContractClassId = ContractClassId(0x41181337), .initializationHash = FF(0x111111), .publicKeys = { .masterNullifierPublicKey = AffinePoint( @@ -74,7 +75,8 @@ TEST(AvmInputsTest, Deserialization) .exists = false, .salt = FF(0xdead0000), .deployer = AztecAddress(0x000020), - .contractClassId = ContractClassId(0x51181337), + .currentContractClassId = ContractClassId(0x51181337), + .originalContractClassId = ContractClassId(0x51181337), .initializationHash = FF(0x222222), .publicKeys = { .masterNullifierPublicKey = AffinePoint( diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin index 703290cccd6..a94f2fb0056 100644 Binary files a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin and b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin differ diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_db.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_db.cpp index 7a739d8e185..b86fd7fd082 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_db.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_db.cpp @@ -22,7 +22,7 @@ ContractInstance HintedRawDataDB::get_contract_instance(const AztecAddress& addr .address = contract_instance_hint.address, .salt = contract_instance_hint.salt, .deployer_addr = contract_instance_hint.deployer, - .contract_class_id = contract_instance_hint.contractClassId, + .contract_class_id = contract_instance_hint.originalContractClassId, .initialisation_hash = contract_instance_hint.initializationHash, .public_keys = PublicKeys{ diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 900acd5636b..284d1099bac 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -114,6 +114,8 @@ library Constants { 24399338136397901754495080759185489776044879232766421623673792970137; uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 14061769416655647708490531650437236735160113654556896985372298487345; + uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE = + 1534834688047131268740281708431107902615560100979874281215533519862; uint256 internal constant MAX_PROTOCOL_CONTRACTS = 7; uint256 internal constant CANONICAL_AUTH_REGISTRY_ADDRESS = 1; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = 2; @@ -122,6 +124,10 @@ library Constants { uint256 internal constant FEE_JUICE_ADDRESS = 5; uint256 internal constant ROUTER_ADDRESS = 6; uint256 internal constant FEE_JUICE_BALANCES_SLOT = 1; + uint256 internal constant UPDATED_CLASS_IDS_SLOT = 1; + uint256 internal constant SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR = 0; + uint256 internal constant SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR = 1; + uint256 internal constant SHARED_MUTABLE_HASH_SEPARATOR = 2; uint256 internal constant DEFAULT_NPK_M_X = 582240093077765400562621227108555700500271598878376310175765873770292988861; uint256 internal constant DEFAULT_NPK_M_Y = @@ -305,4 +311,5 @@ library Constants { uint256 internal constant PROOF_TYPE_ROLLUP_HONK = 5; uint256 internal constant PROOF_TYPE_ROOT_ROLLUP_HONK = 6; uint256 internal constant TWO_POW_64 = 18446744073709551616; + uint256 internal constant DEFAULT_UPDATE_DELAY = 10; } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr index 907a4c5e200..3645a26b11d 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr @@ -1,22 +1,22 @@ use dep::protocol_types::{ address::AztecAddress, + constants::{ + SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR, SHARED_MUTABLE_HASH_SEPARATOR, + SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR, + }, hash::{poseidon2_hash, poseidon2_hash_with_separator}, + shared_mutable::{ + scheduled_delay_change::ScheduledDelayChange, scheduled_value_change::ScheduledValueChange, + }, traits::{FromField, Packable, ToField}, utils::arrays::array_concat, }; use crate::context::{PrivateContext, PublicContext, UnconstrainedContext}; use crate::oracle::storage::storage_read; -use crate::state_vars::{ - shared_mutable::{ - scheduled_delay_change::ScheduledDelayChange, scheduled_value_change::ScheduledValueChange, - }, - storage::Storage, -}; +use crate::state_vars::storage::Storage; use dep::std::mem::zeroed; -pub(crate) mod scheduled_delay_change; -pub(crate) mod scheduled_value_change; mod test; pub struct SharedMutable { @@ -24,11 +24,6 @@ pub struct SharedMutable { storage_slot: Field, } -// Separators separating storage slot of different values within the same state variable -global VALUE_CHANGE_SEPARATOR: u32 = 0; -global DELAY_CHANGE_SEPARATOR: u32 = 1; -global HASH_SEPARATOR: u32 = 2; - // This will make the Aztec macros require that T implements the Serialize trait, and allocate N storage slots to // this state variable. This is incorrect, since what we actually store is: // - a ScheduledValueChange, which requires 1 + 2 * M storage slots, where M is the serialization length of T @@ -74,15 +69,15 @@ where // - a ScheduledDelaChange // - the hash of both of these (via `hash_scheduled_data`) fn get_value_change_storage_slot(self) -> Field { - poseidon2_hash_with_separator([self.storage_slot], VALUE_CHANGE_SEPARATOR) + poseidon2_hash_with_separator([self.storage_slot], SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR) } fn get_delay_change_storage_slot(self) -> Field { - poseidon2_hash_with_separator([self.storage_slot], DELAY_CHANGE_SEPARATOR) + poseidon2_hash_with_separator([self.storage_slot], SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR) } fn get_hash_storage_slot(self) -> Field { - poseidon2_hash_with_separator([self.storage_slot], HASH_SEPARATOR) + poseidon2_hash_with_separator([self.storage_slot], SHARED_MUTABLE_HASH_SEPARATOR) } } @@ -92,6 +87,10 @@ where { pub fn schedule_value_change(self, new_value: T) { + let _value_change = self.schedule_and_return_value_change(new_value); + } + + pub fn schedule_and_return_value_change(self, new_value: T) -> ScheduledValueChange { let mut value_change = self.read_value_change(); let delay_change = self.read_delay_change(); @@ -104,6 +103,8 @@ where value_change.schedule_change(new_value, block_number, current_delay, block_of_change); self.write(value_change, delay_change); + + value_change } pub fn schedule_delay_change(self, new_delay: u32) { diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 8983b35e44a..d9ccdc1d9d6 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -1,13 +1,13 @@ use crate::{ context::{PrivateContext, PublicContext, UnconstrainedContext}, - state_vars::shared_mutable::{ - scheduled_delay_change::ScheduledDelayChange, scheduled_value_change::ScheduledValueChange, - SharedMutable, - }, + state_vars::shared_mutable::SharedMutable, test::helpers::test_environment::TestEnvironment, }; use dep::std::{mem::zeroed, test::OracleMock}; +use protocol_types::shared_mutable::{ + scheduled_delay_change::ScheduledDelayChange, scheduled_value_change::ScheduledValueChange, +}; global new_value: Field = 17; diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index 263c45bba62..bcb624428d2 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -43,6 +43,8 @@ members = [ "contracts/token_blacklist_contract", "contracts/token_bridge_contract", "contracts/uniswap_contract", + "contracts/updatable_contract", + "contracts/updated_contract", "contracts/multi_call_entrypoint_contract", "contracts/static_child_contract", "contracts/static_parent_contract", diff --git a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr index 14de246da73..f85a603804a 100644 --- a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/main.nr @@ -2,13 +2,17 @@ use dep::aztec::macros::aztec; #[aztec] pub contract ContractInstanceDeployer { - use dep::aztec::macros::{events::event, functions::private}; + use dep::aztec::macros::{events::event, functions::{private, public}, storage::storage}; + use dep::aztec::prelude::{Map, SharedMutable}; use dep::aztec::protocol_types::{ address::{AztecAddress, PartialAddress}, - constants::{DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, REGISTERER_CONTRACT_ADDRESS}, + constants::{ + DEFAULT_UPDATE_DELAY, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, + DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE, REGISTERER_CONTRACT_ADDRESS, + }, contract_class_id::ContractClassId, public_keys::PublicKeys, - traits::Serialize, + traits::{Serialize, ToField}, utils::arrays::array_concat, }; use dep::contract_class_registerer::ContractClassRegisterer; @@ -78,6 +82,21 @@ pub contract ContractInstanceDeployer { } } + #[derive(Serialize)] + #[event] + struct ContractInstanceUpdated { + DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE: Field, + address: AztecAddress, + prev_contract_class_id: ContractClassId, + new_contract_class_id: ContractClassId, + block_of_change: u32, + } + + #[storage] + struct Storage { + updated_class_ids: Map, Context>, + } + #[private] fn deploy( salt: Field, @@ -123,4 +142,35 @@ pub contract ContractInstanceDeployer { let padded_log = array_concat(payload, [0; 3]); context.emit_private_log(padded_log); } + + #[public] + fn update(new_contract_class_id: ContractClassId) { + let address = context.msg_sender(); + + assert( + context.nullifier_exists(address.to_field(), context.this_address()), + "msg.sender is not deployed", + ); + + assert( + context.nullifier_exists(new_contract_class_id.to_field(), REGISTERER_CONTRACT_ADDRESS), + "New contract class is not registered", + ); + + let scheduled_value_update = storage + .updated_class_ids + .at(address) + .schedule_and_return_value_change(new_contract_class_id); + let (prev_contract_class_id, block_of_change) = scheduled_value_update.get_previous(); + + let event = ContractInstanceUpdated { + DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE, + address, + prev_contract_class_id, + new_contract_class_id, + block_of_change, + }; + + context.emit_public_log(event); + } } diff --git a/noir-projects/noir-contracts/contracts/updatable_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/updatable_contract/Nargo.toml new file mode 100644 index 00000000000..77fcc50875c --- /dev/null +++ b/noir-projects/noir-contracts/contracts/updatable_contract/Nargo.toml @@ -0,0 +1,10 @@ +[package] +name = "updatable_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } +value_note = { path = "../../../aztec-nr/value-note" } +contract_instance_deployer = { path = "../contract_instance_deployer_contract" } diff --git a/noir-projects/noir-contracts/contracts/updatable_contract/src/main.nr b/noir-projects/noir-contracts/contracts/updatable_contract/src/main.nr new file mode 100644 index 00000000000..d9cef25daf4 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/updatable_contract/src/main.nr @@ -0,0 +1,56 @@ +use dep::aztec::macros::aztec; + +#[aztec] +contract Updatable { + use aztec::encrypted_logs::log_assembly_strategies::default_aes128::note::encode_and_encrypt_note; + use aztec::macros::{functions::{initializer, private, public}, storage::storage}; + use aztec::prelude::{PrivateMutable, PublicMutable}; + + use aztec::protocol_types::{ + constants::DEPLOYER_CONTRACT_ADDRESS, + contract_class_id::ContractClassId, + traits::{Hash, Serialize}, + }; + use contract_instance_deployer::ContractInstanceDeployer; + use value_note::value_note::ValueNote; + + #[storage] + struct Storage { + private_value: PrivateMutable, + public_value: PublicMutable, + } + + #[initializer] + #[private] + fn initialize(initial_value: Field) { + let owner = context.msg_sender(); + let mut new_value = ValueNote::new(initial_value, owner); + storage.private_value.initialize(&mut new_value).emit(encode_and_encrypt_note( + &mut context, + owner, + owner, + )); + Updatable::at(context.this_address()).set_public_value(initial_value).enqueue(&mut context); + } + + #[public] + fn set_public_value(new_value: Field) { + storage.public_value.write(new_value); + } + + #[private] + fn update_to(new_class_id: ContractClassId) { + ContractInstanceDeployer::at(DEPLOYER_CONTRACT_ADDRESS).update(new_class_id).enqueue( + &mut context, + ); + } + + unconstrained fn get_private_value() -> pub Field { + storage.private_value.view_note().value + } + + unconstrained fn get_public_value() -> pub Field { + storage.public_value.read() + } + +} diff --git a/noir-projects/noir-contracts/contracts/updated_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/updated_contract/Nargo.toml new file mode 100644 index 00000000000..dd8dda4538a --- /dev/null +++ b/noir-projects/noir-contracts/contracts/updated_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "updated_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } +value_note = { path = "../../../aztec-nr/value-note" } diff --git a/noir-projects/noir-contracts/contracts/updated_contract/src/main.nr b/noir-projects/noir-contracts/contracts/updated_contract/src/main.nr new file mode 100644 index 00000000000..bf2747ed8c2 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/updated_contract/src/main.nr @@ -0,0 +1,42 @@ +use dep::aztec::macros::aztec; + +#[aztec] +contract Updated { + use aztec::encrypted_logs::log_assembly_strategies::default_aes128::note::encode_and_encrypt_note; + use aztec::macros::{functions::{private, public}, storage::storage}; + use aztec::prelude::{PrivateMutable, PublicMutable}; + + use aztec::protocol_types::traits::Hash; + use value_note::value_note::ValueNote; + + #[storage] + struct Storage { + private_value: PrivateMutable, + public_value: PublicMutable, + } + + #[public] + fn set_public_value() { + storage.public_value.write(27); + } + + #[private] + fn set_private_value() { + let owner = context.msg_sender(); + let mut new_note = ValueNote::new(27, owner); + storage.private_value.replace(&mut new_note).emit(encode_and_encrypt_note( + &mut context, + owner, + owner, + )); + } + + unconstrained fn get_private_value() -> pub Field { + storage.private_value.view_note().value + } + + unconstrained fn get_public_value() -> pub Field { + storage.public_value.read() + } + +} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-init/Prover.toml b/noir-projects/noir-protocol-circuits/crates/private-kernel-init/Prover.toml index 9877bddbb29..ce109fd9101 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-init/Prover.toml +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-init/Prover.toml @@ -1,13 +1,13 @@ -vk_tree_root = "0x130ae70545763dadb59caeb820400cffc41df59bcc2b8731917644f247717686" -protocol_contract_tree_root = "0x2f9edcbdd0cfc67764aa32cdef9ea44f2914b44bfd9cf77d518a5ac172e7f9e4" +vk_tree_root = "0x215f80cda67ebdb1bca149b200664f9f453218edcd6d74840eefbcbdb653086f" +protocol_contract_tree_root = "0x0765c36c56d790ae7929d668acf0f5c38cc5e898c893b538bd784a40d7018098" is_private_only = false first_nullifier_hint = "0x0000000000000000000000000000000000000000000000000000000000000000" [tx_request] -args_hash = "0x0af1cbd9eb90baf47ec9d1306eb8877ea38b9a383a2ab2145c04cae5ac01048d" +args_hash = "0x210d7c74fe1bfceb684c0a39323a8eb96a33402e20e9716d7616a9ca0cabb2c6" [tx_request.origin] - inner = "0x238db6644a299b7b33993c163a0b3f3f3aaeca32f9d0f9d0328c1615d7efb8a5" + inner = "0x1ec8af141dd61668d55b47def5c69e3b97742187cfa911c55deb13ff8c4b4390" [tx_request.tx_context] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" @@ -23,7 +23,7 @@ l2_gas = "0x00000000000000000000000000000000000000000000000000000000005b8d80" [tx_request.tx_context.gas_settings.max_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" -fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd0959c" +fee_per_l2_gas = "0x00000000000000000000000000000000000000000000000000000012e9bb9bf7" [tx_request.tx_context.gas_settings.max_priority_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -35,18 +35,8 @@ fee_per_l2_gas = "0x000000000000000000000000000000000000000000000000000000000000 [tx_request.function_data.selector] inner = "0x0000000000000000000000000000000000000000000000000000000027e740b2" -[private_call] -contract_class_artifact_hash = "0x12101840a1f7fa6ea7c71191b979c27a590371482158e8464922a3c0684a7f5a" -contract_class_public_bytecode_commitment = "0x0000000000000000000000000000000000000000000000000000000000000000" -protocol_contract_sibling_path = [ - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000" -] -acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - - [private_call.vk] - key = [ +[private_call.vk] +key = [ "0x0000000000000000000000000000000000000000000000000000000000100000", "0x0000000000000000000000000000000000000000000000000000000000000010", "0x00000000000000000000000000000000000000000000000000000000000328b1", @@ -70,30 +60,30 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000004c0088c6304eea8b30f3bc234ba61c5e40", - "0x000000000000000000000000000000000018fe184707f8e3907ddf0a84ab96c9", - "0x000000000000000000000000000000e2e3beba971c1a3c94b236df2fa85e1b8d", - "0x000000000000000000000000000000000014ae8d966f0a7dad8d960083be6ab8", - "0x00000000000000000000000000000025cd859bd60a775d16af86fa82a7438d19", - "0x0000000000000000000000000000000000295a994fbbff1ec0099d5f3b67b9e6", - "0x0000000000000000000000000000007b1565059fd2ef91d75b15e5da56a2d91a", - "0x0000000000000000000000000000000000078bcd82da3c09b65f29f0c8c7bcea", - "0x0000000000000000000000000000002dbc692a31dee00e6a57f09a3d916429e1", - "0x0000000000000000000000000000000000200d4c50030bc07e58990e7ba93af5", - "0x0000000000000000000000000000000a0cc89c14169147a1dd0441312dde067c", - "0x000000000000000000000000000000000025946f08ac4b4019a384f69fc6b251", - "0x000000000000000000000000000000ad7cdd97ad88d7760771cdd0b49e277acd", - "0x0000000000000000000000000000000000218bae9a7c2b611799a9ec996044b2", - "0x0000000000000000000000000000009cb023d832ed90a916bb80d08bdae6d622", - "0x00000000000000000000000000000000003014603281bfb76e1767d49d9c74e1", - "0x00000000000000000000000000000004b0a492dfe5e11656683f5ebb80600409", - "0x00000000000000000000000000000000000a35de998e82206a2bce906a8f83a7", - "0x00000000000000000000000000000015ffdf7657847adcffb798afbd8eb80911", - "0x00000000000000000000000000000000002fe4db1f117da253da2b4a7b92d9f9", - "0x000000000000000000000000000000cf5991517a9828a56e8ea78f14938b152b", - "0x00000000000000000000000000000000002aeb8de879466b711e5797c95c3f19", - "0x000000000000000000000000000000dda34135cb406c7bec366bd0fe0596d963", - "0x0000000000000000000000000000000000165aac32a660f4b3ae1e6dbeaf5f9a", + "0x000000000000000000000000000000775836e31e53bda7e93c53422d1a8e3cd5", + "0x00000000000000000000000000000000000377028cc87fade6f8bcb734568961", + "0x000000000000000000000000000000ca7fd0674a7ba20833295c416420d2e7c1", + "0x0000000000000000000000000000000000265f70ffb78f42ca9db527ccc2a0d9", + "0x0000000000000000000000000000005e0a4cdef34a693652cf33bf776af83c0a", + "0x00000000000000000000000000000000000f776e3a088ddf6cb549da8a207d56", + "0x00000000000000000000000000000071ff3e700b2f8f991decc32319afcfe8c3", + "0x00000000000000000000000000000000000836ec28bbf5e572587cc47c416a2b", + "0x00000000000000000000000000000060d141d024dee23b3a8dd1aa98f224c440", + "0x000000000000000000000000000000000011861e38b0457ea34d870b4f3e4c23", + "0x00000000000000000000000000000048e0200af4523949ed2693783aa68ffc4c", + "0x00000000000000000000000000000000002093f016018e7bb8407e0e76fb1629", + "0x0000000000000000000000000000004cb2c6c83b90ffe5cbec441a7ae6b45bc7", + "0x00000000000000000000000000000000000bc3b6158749fe64e955f1b185f1d0", + "0x000000000000000000000000000000a51a9497905a681a2e9d6215c7a1abaed8", + "0x0000000000000000000000000000000000258d8b8588ce2899dd49549c9ada73", + "0x000000000000000000000000000000ac4f33f32ec385d0efd7bd413754adf63d", + "0x0000000000000000000000000000000000069613efcc8e9d4ee400a06a730118", + "0x0000000000000000000000000000005fcc2a645b50ba1b71326384b2579a8af1", + "0x00000000000000000000000000000000000b27b8419676e7a84a9d38ec798da4", + "0x000000000000000000000000000000657ca0d82735e356b2a5d55b6ac684655b", + "0x00000000000000000000000000000000000dbcd5763887e61277347c404d3980", + "0x0000000000000000000000000000004413809de081762acd74fc88ef5a7c5a99", + "0x0000000000000000000000000000000000052cbb84afbb82c1f5432578e8f7c7", "0x000000000000000000000000000000552393d09533e1b2004bd84e9f8a53a88f", "0x00000000000000000000000000000000000456bb701b5b0b463bbcf0c4fe7190", "0x00000000000000000000000000000074156481e9a3f8f091d30db1003a6daab4", @@ -102,10 +92,10 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x00000000000000000000000000000000000d56bbef147278fdc057f9a336d984", "0x000000000000000000000000000000f11f3eaed8726026211d2ee0f83e32e453", "0x0000000000000000000000000000000000291fbbe0b7f6f2823d5469cf981a1e", - "0x00000000000000000000000000000037d316dad16af640351c394dfaa84aff9c", - "0x00000000000000000000000000000000001c85c5c2ad15dffe02c925482f12f4", - "0x000000000000000000000000000000a896f37773d96a9bf91454a5cf4e060510", - "0x00000000000000000000000000000000001351fb0185f0803b26cc5ad05e04cc", + "0x000000000000000000000000000000ffd28cf9ba1417c1caf418b43a3de9d434", + "0x000000000000000000000000000000000015449830399d0f54c492fcd591732f", + "0x000000000000000000000000000000fd493f730fa26592af1e4c7af7ec5321a5", + "0x00000000000000000000000000000000000bf622ffdd69efded5b1ad1d42d140", "0x000000000000000000000000000000f7a57d8eb28c5d23873376972e0630ac39", "0x00000000000000000000000000000000001b55bec64f61aa2803559d6a766e60", "0x000000000000000000000000000000d20d9a80ac0aa69cbff40f2e4dca6eebce", @@ -126,38 +116,38 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x00000000000000000000000000000000001dad3f4e78044bf6197cbd3e376f67", "0x000000000000000000000000000000d68a49412f45d61ef4fa8a13437267f9de", "0x0000000000000000000000000000000000243adeaa8a631cdf7bc2586150dba2", - "0x00000000000000000000000000000027ae20d03e8284ebed50ab3678a8233766", - "0x000000000000000000000000000000000006dbeac901d1d3f41f95e5035c6705", - "0x000000000000000000000000000000ad3b3291a3169e7ace495a1b5cf3e33cc7", - "0x0000000000000000000000000000000000121859ca70ca69522d832967f8ad6d", - "0x000000000000000000000000000000515888890c39d0400406df1cf1ca98d7f3", - "0x00000000000000000000000000000000000f2058754cee26d7bfa1146e43d42a", - "0x00000000000000000000000000000034dacea200db47dae167a253278b0de20c", - "0x00000000000000000000000000000000002c9303ef3980ffb97e934ad783839b", - "0x000000000000000000000000000000e29d8d89a01e6c58f90ddbe1df06ad7bca", - "0x00000000000000000000000000000000002685373d3ca435751faec65d4088f3", - "0x00000000000000000000000000000097666c7ded9448c11f039d951224aaa41f", - "0x00000000000000000000000000000000002f45f7a3625dd9c7ff1846c21ad892", - "0x00000000000000000000000000000034e5173b2c28747e2a4cbb73440fcdb6f3", - "0x0000000000000000000000000000000000217ab01a6641c33b74eceb662735a7", - "0x000000000000000000000000000000e6cb0f0c0475b4f5a0d2a3343d63427f6b", - "0x000000000000000000000000000000000016ad961279ad0b14b65851d17475af", - "0x000000000000000000000000000000e9e95800ed53e815d4da8e3ca853cdde0b", - "0x00000000000000000000000000000000001fcf583d7e8f26d95fa14471ab5c24", - "0x000000000000000000000000000000b3c8aa401c870791e6fff92dd7c359af78", - "0x000000000000000000000000000000000010e505ea341011c73456a4ebbadce0", - "0x00000000000000000000000000000031d83fc6b03a4e6ce7fdf44ea6864d6e9c", - "0x0000000000000000000000000000000000012604b18752180b6907e717aec25f", - "0x000000000000000000000000000000d7e8a71216707d312d6c0b3e00d3640074", - "0x0000000000000000000000000000000000169f0c41f9894810eb34bab4bd7542", - "0x0000000000000000000000000000001cb162d604ac88bd1f831d3965d5a512a9", - "0x000000000000000000000000000000000002a2199182adc8326da8b7a7afa479", - "0x000000000000000000000000000000ed232a9b53a3bd2f9a07072f01ca8cead3", - "0x00000000000000000000000000000000001bb5b1d09a76524b5117c8840cb9cf", - "0x0000000000000000000000000000001e3da8bfd595ead94cfa4b6fa2233d4d67", - "0x00000000000000000000000000000000001c678f6e902335b652843f66c18a73", - "0x000000000000000000000000000000036e7402769cf31acb416b162dd64767ca", - "0x00000000000000000000000000000000001c50319d4b4f116bc857b94f714bb8", + "0x00000000000000000000000000000004b49a102241618edbd23c93ecba625dc6", + "0x00000000000000000000000000000000002abb2d064545acb95c5e80977c818a", + "0x000000000000000000000000000000c5c6b09b6c8bf03488b12695e8fc70fe22", + "0x00000000000000000000000000000000001b15326a839c5e7c221e480720c36b", + "0x000000000000000000000000000000662c2473f0e8ab59f734804c9a69ae121f", + "0x0000000000000000000000000000000000199ef1eb9b40bb93923a40577d95e5", + "0x0000000000000000000000000000009cf99efb8d678b82439fbbaf7774e32c4e", + "0x00000000000000000000000000000000000c46231fdc6142f2f2533fa6eabbc2", + "0x000000000000000000000000000000b639d851190c1f37fd178a03add2725676", + "0x00000000000000000000000000000000000e1aa64856c9d51fdee716f00ec474", + "0x00000000000000000000000000000052bbc13d7f0dd3f0760ac75330f189b904", + "0x00000000000000000000000000000000000d50e12d34b1fea28df38785df176b", + "0x000000000000000000000000000000b7c5fa0eab0e9af427251a0f0e90d4390f", + "0x00000000000000000000000000000000000d4b6c7df437152d9f4950640b838a", + "0x000000000000000000000000000000f69d8a955bc29d1e73873aaf4f41e26ab9", + "0x00000000000000000000000000000000002ecda78bf9dd30a78b5c3ba5684e12", + "0x000000000000000000000000000000ea75e11432b4e3d2184225704fb2643f11", + "0x00000000000000000000000000000000000b2eda7abe3b05d227567bd9eb64c1", + "0x0000000000000000000000000000005c2d319d1d8c43fdb524437ca0747c4800", + "0x00000000000000000000000000000000002dc52732b4471ab1e0484dcbde4702", + "0x000000000000000000000000000000b6f7b03b7de4cbe83ec0cec7d17859931c", + "0x000000000000000000000000000000000014f079a015f56c9f46ad5957d9b7e8", + "0x0000000000000000000000000000000d70afaf2f2379249a103b2ef8632c7c8e", + "0x00000000000000000000000000000000000e949a273b7740556f42964e79538c", + "0x000000000000000000000000000000f486b1eeaf7647aae37ddaf8f9617c1eec", + "0x00000000000000000000000000000000000a50e99a1f25f251463473bcc377c2", + "0x0000000000000000000000000000004bc3f9da26310273537951a19b31c298fa", + "0x000000000000000000000000000000000017faa947354da834c133db980f5462", + "0x00000000000000000000000000000062018fbe858d3180b2d04e56911a792ffb", + "0x00000000000000000000000000000000002b1262a9a690c3eb425dbe84386d28", + "0x0000000000000000000000000000004e05d50aa0f686d76358ce7e2ce23897a3", + "0x00000000000000000000000000000000000528a2c0f2014cdf1b535f8f663d86", "0x00000000000000000000000000000084a9b3527cd2ebff62b245a04aab258f92", "0x00000000000000000000000000000000000a85019e1252699312cbd5ec6a23b2", "0x00000000000000000000000000000000b5eee72336430c3feb7da6b8b57e1551", @@ -166,10 +156,10 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x000000000000000000000000000000000000b34e0082bc5aed819a81bb36744c", "0x000000000000000000000000000000beb969e0f2c7856270dc5fda2c5d399dcc", "0x00000000000000000000000000000000000013aea3bcc0841ec6d94b285f1beb", - "0x000000000000000000000000000000b64bcb23012aef7421ae1b97a9a33f941b", - "0x000000000000000000000000000000000005aa1eeb4d3694a5063763f82318fd", - "0x000000000000000000000000000000b8d3152c9ac8136776bbd21b731aefc643", - "0x00000000000000000000000000000000001b3938ba03ad7d153b1ffb7d6a18de", + "0x0000000000000000000000000000002671782a93372aad369530ae4b75c22bdb", + "0x000000000000000000000000000000000002d9f0465ef4b2b116d4b88625344f", + "0x00000000000000000000000000000026e9839942d72920141febdab07e4c20c3", + "0x0000000000000000000000000000000000265f0c70536ec02f7c9be4ce19c29d", "0x0000000000000000000000000000003a339e8cb8c648d07c34ddcb4ef4452783", "0x000000000000000000000000000000000027807a4f7b23d9cc1c865ef9930999", "0x000000000000000000000000000000bc4fd810c781d7b239a47a086361686edc", @@ -191,43 +181,112 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x00000000000000000000000000000078f4b3bc61f19d6e7069359bbf47e7f907", "0x00000000000000000000000000000000002d7c18a93c3dae58809faaeec6a86a" ] - hash = "0x03be9a2387e1b3804b26f02b835f4e67c4cd5ea27134755127dc63d07482da65" +hash = "0x2f8fc4817e72f3df76a1933723114dcfa15854f5fe10178c458bdff5aca6d783" + +[private_call.verification_key_hints] +contract_class_artifact_hash = "0x093b3ef6aa64f87b0ebe1f85916850de67ca43a883c2f1916706dc7e02236f38" +contract_class_public_bytecode_commitment = "0x0000000000000000000000000000000000000000000000000000000000000000" +protocol_contract_sibling_path = [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" +] +acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +updated_class_id_value_change = [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" +] +updated_class_id_delay_change = [ + "0x0000000000000000000000000000000000000000000000000000000000000000" +] - [private_call.function_leaf_membership_witness] + [private_call.verification_key_hints.function_leaf_membership_witness] leaf_index = "0" sibling_path = [ - "0x0e050f73231cf94fe1f1fe29a407514ec3398942745d44df55ec6ef5b02ae7d5", - "0x2eaf5bf1003f0f97f98a0d49a64cef3c812831098bde3da130ca915b727a48cc", + "0x2c5cf3eba88f6b97be1e9f373f62c5c317ea9693f54ad1b03e826cf14e83c868", + "0x22d0c6ab9e48b1f8fd239d09ff2f5cfbff9501b21033f8c7a46d92b4fb6d0a54", "0x0e1ce4f11f4d51a7d3136abbd625315fff3876d322e46ad8401da96f8e43a614", "0x1a0ca5eecb1430479902264f04e557160a4666fb42bb8b283c6397e3d17ac937", "0x2a6595890719fef7967c209488728aa5342438ba52058a3c770202f55acf6854" ] -[private_call.public_keys.npk_m.inner] -x = "0x2bcd3866b5cfda1e4ec60ac07d18d963d3dc0312565f0bd87c57bee8b11a3d79" -y = "0x2a1698b41b5275c81f388c8197320a7f2c3ea6e00eff1b6fc766d37438813d91" +[private_call.verification_key_hints.public_keys.npk_m.inner] +x = "0x1f1226734a3242043d1a672b8da63965ef24fd07760c199affb79df17b639297" +y = "0x0c56ef273702885eac2aeff918d1002e1bd3a7f76f5309b378899bf4be901fed" is_infinite = false -[private_call.public_keys.ivpk_m.inner] -x = "0x29bf6934ce2dad97372aebc3996999ac75f36861521a7f9e3e4015398b6dae0c" -y = "0x2bad97bc7a60a7eb91f74fcac835078f2d349b76baebb04a4c67ab38f47e7d8d" +[private_call.verification_key_hints.public_keys.ivpk_m.inner] +x = "0x24048e204a785da3c29a9b747202339a0e0b3616a02f4a77d43ec645b69eb070" +y = "0x2a6b14288b5a59f3381f2d3bbc7fb6e8d98872dea2f9c9ce11d43446a36cad6a" is_infinite = false -[private_call.public_keys.ovpk_m.inner] -x = "0x0466d31125860343f098277c6fb01d8402a0ac4cfccf8acb81eee53b498fe3d4" -y = "0x033776c04e1be74c10a298b4216e5d73961c833bf056e3aabbe8cb14e43345b6" +[private_call.verification_key_hints.public_keys.ovpk_m.inner] +x = "0x29e42afcfdd5c781cc8d39e5206c18b7d060c698f56ae2c8984c8bee8a4e533e" +y = "0x1c4f44dc6d039351313a5a86a7b84cfaf151c382a20d05275acffc2a5abceca8" is_infinite = false -[private_call.public_keys.tpk_m.inner] -x = "0x15383ff9964199e59a676e836d9d05724afc61f29aecbdac4869a346b01716aa" -y = "0x14cc049d171836530ba20dc7e80010b669cb37456031212c57fb8dabaf9bdf3b" +[private_call.verification_key_hints.public_keys.tpk_m.inner] +x = "0x0537d195100c6db525ba1095e486199822ee049c7aa395b8bdd7d0ade8bac49a" +y = "0x29fb1c7a3dbdb327beda02f2f9070f6bee54e8f91c9c6efb2fb76170bd77c0c9" is_infinite = false - [private_call.salted_initialization_hash] - inner = "0x072ad0fe9aedb284a12f22f48c156e660f91fc0d318e184d2386f6be5148a15d" + [private_call.verification_key_hints.salted_initialization_hash] + inner = "0x0cc74be0be53b6aa866f4a47354bd7c1fe068061c0a43d963f486fb3fef9e591" + + [private_call.verification_key_hints.updated_class_id_witness] + leaf_index = "146" + sibling_path = [ + "0x2f64a8f5ffc6aa56ebc2a35e599736586416d249555f0e94d5d62398eeba5c95", + "0x25c335a21e4880d1111d64509af6c26118bc8373899216ec2b49214eb08e1733", + "0x1630f40cf7d179178f2c9620ecec29c82259e400053d6076ada737bd342bbeb2", + "0x19315696be2ca3ad03243b7562e52467e3960a5596990f0d0766bcf1218b66fc", + "0x00e67ba426c1d84d9b734f5ffb63978cc4809364e587ce009283b879c396105f", + "0x120157cfaaa49ce3da30f8b47879114977c24b266d58b0ac18b325d878aafddf", + "0x01c28fe1059ae0237b72334700697bdf465e03df03986fe05200cadeda66bd76", + "0x21874f891782d18ffd9fa527f87b05eca98ef8eeafa2d21e91f0c1f59b8e31f9", + "0x067243231eddf4222f3911defbba7705aff06ed45960b27f6f91319196ef97e1", + "0x1849b85f3c693693e732dfc4577217acc18295193bede09ce8b97ad910310972", + "0x2a775ea761d20435b31fa2c33ff07663e24542ffb9e7b293dfce3042eb104686", + "0x0f320b0703439a8114f81593de99cd0b8f3b9bf854601abb5b2ea0e8a3dda4a7", + "0x0d07f6e7a8a0e9199d6d92801fff867002ff5b4808962f9da2ba5ce1bdd26a73", + "0x1c4954081e324939350febc2b918a293ebcdaead01be95ec02fcbe8d2c1635d1", + "0x0197f2171ef99c2d053ee1fb5ff5ab288d56b9b41b4716c9214a4d97facc4c4a", + "0x2b9cdd484c5ba1e4d6efcc3f18734b5ac4c4a0b9102e2aeb48521a661d3feee9", + "0x14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3", + "0x071d7627ae3b2eabda8a810227bf04206370ac78dbf6c372380182dbd3711fe3", + "0x2fdc08d9fe075ac58cb8c00f98697861a13b3ab6f9d41a4e768f75e477475bf5", + "0x20165fe405652104dceaeeca92950aa5adc571b8cafe192878cba58ff1be49c5", + "0x1c8c3ca0b3a3d75850fcd4dc7bf1e3445cd0cfff3ca510630fd90b47e8a24755", + "0x1f0c1a8fb16b0d2ac9a146d7ae20d8d179695a92a79ed66fc45d9da4532459b3", + "0x038146ec5a2573e1c30d2fb32c66c8440f426fbd108082df41c7bebd1d521c30", + "0x17d3d12b17fe762de4b835b2180b012e808816a7f2ff69ecb9d65188235d8fd4", + "0x0e1a6b7d63a6e5a9e54e8f391dd4e9d49cdfedcbc87f02cd34d4641d2eb30491", + "0x09244eec34977ff795fc41036996ce974136377f521ac8eb9e04642d204783d2", + "0x1646d6f544ec36df9dc41f778a7ef1690a53c730b501471b6acd202194a7e8e9", + "0x064769603ba3f6c41f664d266ecb9a3a0f6567cd3e48b40f34d4894ee4c361b3", + "0x1595bb3cd19f84619dc2e368175a88d8627a7439eda9397202cdb1167531fd3f", + "0x2a529be462b81ca30265b558763b1498289c9d88277ab14f0838cb1fce4b472c", + "0x0c08da612363088ad0bbc78abd233e8ace4c05a56fdabdd5e5e9b05e428bdaee", + "0x14748d0241710ef47f54b931ac5a58082b1d56b0f0c30d55fb71a6e8c9a6be14", + "0x0b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d", + "0x2c45bb0c3d5bc1dc98e0baef09ff46d18c1a451e724f41c2b675549bb5c80e59", + "0x121468e6710bf1ffec6d0f26743afe6f88ef55dab40b83ca0a39bc44b196374c", + "0x2042c32c823a7440ceb6c342f9125f1fe426b02c527cd8fb28c85d02b705e759", + "0x0d582c10ff8115413aa5b70564fdd2f3cefe1f33a1e43a47bc495081e91e73e5", + "0x0f55a0d491a9da093eb999fa0dffaf904620cbc78d07e63c6f795c5c7512b523", + "0x21849764e1aa64b83a69e39d27eedaec2a8f97066e5ddb74634ffdb11388dd9a", + "0x2e33ee2008411c04b99c24b313513d097a0d21a5040b6193d1f978b8226892d6" +] + + [private_call.verification_key_hints.updated_class_id_leaf] + slot = "0x201f7703728762b9c55cdc1d8438429b2212ac6852f647d7e3bacdfa25add655" + value = "0x00007212d2e47049672ac4c925cad339853b3c847c64dcb44f9de9a8c8543021" + next_slot = "0x23b7ce153ad5ff3a10648b3830d3032863825f835af06b86c2ef45f69814a568" + next_index = "0x0000000000000000000000000000000000000000000000000000000000000098" [app_public_inputs] -args_hash = "0x0af1cbd9eb90baf47ec9d1306eb8877ea38b9a383a2ab2145c04cae5ac01048d" +args_hash = "0x210d7c74fe1bfceb684c0a39323a8eb96a33402e20e9716d7616a9ca0cabb2c6" returns_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" start_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000001" end_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000004" @@ -245,13 +304,13 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" inner = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000" [app_public_inputs.call_context.contract_address] - inner = "0x238db6644a299b7b33993c163a0b3f3f3aaeca32f9d0f9d0328c1615d7efb8a5" + inner = "0x1ec8af141dd61668d55b47def5c69e3b97742187cfa911c55deb13ff8c4b4390" [app_public_inputs.call_context.function_selector] inner = "0x0000000000000000000000000000000000000000000000000000000027e740b2" [[app_public_inputs.note_hash_read_requests]] - value = "0x0493504d7e2628c6f4c13f91240ac2aeff1175f772039af764ebced0da4326e4" + value = "0x2f3068fb5d1037dfe562bd7425700f2637cd0a6c708bc0068d9af4e6313f542e" counter = "0x0000000000000000000000000000000000000000000000000000000000000002" [[app_public_inputs.note_hash_read_requests]] @@ -793,13 +852,13 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.public_call_requests.inner] is_static_call = true - args_hash = "0x0f0616825d1d1592b7dfa8ac06c7ddeaeadc7d34a61a1632604c73588bd09ea1" + args_hash = "0x0bba38d4c917e0eb36dcd048655be2e12cfb1dbf9d3b7d8b6dbe3edc83d61f63" [app_public_inputs.public_call_requests.inner.msg_sender] - inner = "0x238db6644a299b7b33993c163a0b3f3f3aaeca32f9d0f9d0328c1615d7efb8a5" + inner = "0x1ec8af141dd61668d55b47def5c69e3b97742187cfa911c55deb13ff8c4b4390" [app_public_inputs.public_call_requests.inner.contract_address] - inner = "0x1f3f85b45ea6c7be79b6e4948002182167732a202d83e1dba67b53f676583c83" + inner = "0x28da25b414c15e3780cd14c55a77ee681255341ae6ba104c8ea9b4dc621c6e54" [app_public_inputs.public_call_requests.inner.function_selector] inner = "0x00000000000000000000000000000000000000000000000000000000d5441b0d" @@ -1493,16 +1552,16 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" length = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.historical_header] - total_fees = "0x0000000000000000000000000000000000000000000000000003a83222de8a00" + total_fees = "0x0000000000000000000000000000000000000000000000000003a831c6a84280" total_mana_used = "0x0000000000000000000000000000000000000000000000000000000000004a40" [app_public_inputs.historical_header.last_archive] - root = "0x082bf9a6382021ca9fa204bdbc71fd5536d84358e57a61f129fdf5bfb228c06f" + root = "0x16608d96b4901a721581dc02e113b68871e027b09c54811bba56924a4494f9ce" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000008" [app_public_inputs.historical_header.content_commitment] num_txs = "0x0000000000000000000000000000000000000000000000000000000000000001" - blobs_hash = "0x00310bb50f2202183669b8a85c09986fd69c203fee7d32e06dd1b46c84b20a7d" + blobs_hash = "0x00d3ca5f4aa587c294535143deef5eaf88278e8780cd3f0e7c577bd5e40e2450" in_hash = "0x00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c" out_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -1511,33 +1570,33 @@ root = "0x2e33ee2008411c04b99c24b313513d097a0d21a5040b6193d1f978b8226892d6" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [app_public_inputs.historical_header.state.partial.note_hash_tree] -root = "0x2596f3d6b05b7055c3c5ab89de6e270b653cb30c3dd6b9cfd230ff41b5ffa623" +root = "0x2a44514b808f61b25e71d1f2c3f65aefd446d41d9a9a36ec89e709be5b82b598" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000240" [app_public_inputs.historical_header.state.partial.nullifier_tree] -root = "0x15205da8ec6fc0317cee4482de04084768078d73397e7f466c11c45c2cb66bac" +root = "0x0867e7af1fb7ccdd91be072954eca54426bfbcd46a83afc96a71ac930a76d165" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000002c0" [app_public_inputs.historical_header.state.partial.public_data_tree] -root = "0x2841d0ae135bd8bf3761f919c16b9e5cf7ad4b3035328f65bf84ee2dc599aed6" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000097" +root = "0x1b1e369f0479fa96c779683d2cc01b3002931d28e48717c5d16450dfb8003bf2" +next_available_leaf_index = "0x000000000000000000000000000000000000000000000000000000000000009a" [app_public_inputs.historical_header.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" version = "0x0000000000000000000000000000000000000000000000000000000000000001" block_number = "0x0000000000000000000000000000000000000000000000000000000000000008" slot_number = "0x0000000000000000000000000000000000000000000000000000000000000022" - timestamp = "0x00000000000000000000000000000000000000000000000000000000678ac01a" + timestamp = "0x0000000000000000000000000000000000000000000000000000000067a3b983" [app_public_inputs.historical_header.global_variables.coinbase] - inner = "0x00000000000000000000000017ad47b9f14dc201975cca47fea765e5d42f0b72" + inner = "0x00000000000000000000000071de811f9b5b8d82122993e4f401c4776c6d8029" [app_public_inputs.historical_header.global_variables.fee_recipient] inner = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.historical_header.global_variables.gas_fees] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" - fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd3a5e8" + fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd267fa" [app_public_inputs.tx_context] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" @@ -1553,7 +1612,7 @@ l2_gas = "0x00000000000000000000000000000000000000000000000000000000005b8d80" [app_public_inputs.tx_context.gas_settings.max_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" -fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd0959c" +fee_per_l2_gas = "0x00000000000000000000000000000000000000000000000000000012e9bb9bf7" [app_public_inputs.tx_context.gas_settings.max_priority_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-inner/Prover.toml b/noir-projects/noir-protocol-circuits/crates/private-kernel-inner/Prover.toml index 00bc295c0d2..2fa5a095e0e 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-inner/Prover.toml +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-inner/Prover.toml @@ -1,12 +1,12 @@ [previous_kernel] vk_index = "0x0000000000000000000000000000000000000000000000000000000000000000" vk_path = [ - "0x16cdb92a1804137932723311e0102e917f249e2d8a96faeed8aad4d04a5249c5", - "0x03325592c83286ae329c189ebf786a670f3e725ee33f2d3f1b896d42cff8bc2d", - "0x1055d2be8c827009ac1c36a3d984a543c20c6c6783d9783fb57c63c544a3f539", - "0x2c8b742477af94eb3d371b8aeef0f9a8d930466cfe240ca1b7c504b0b4492d22", - "0x126bb13ce4b07681f511f3a23d88cb7cfe082a76730abd5b4be5c4da3b5c51fb", - "0x050dc6390aace9c6e873b762c65e7d6a21e78e64e5ad6cc7c8fd5e69315ab5fe" + "0x27e31657787ee9084b1882de6a70a2bca9a1d87b3abf4409493889e7be3f0f1e", + "0x19275c3c902286390bf9165a8a58adfa82283972fbb5d8c55d8faf01ec3e5a0e", + "0x141a8fa2a72efb925a16ea40068eda27c5a794655bc1338896e7278ea168d7de", + "0x1559b780e36ec75bb3b5e4bc856db756c041090b09c4a6bec63ff5d830b2d3b7", + "0x085de27a70b1e644dba1f38475bc3cf44d2db93e32296c875e1b4e7435720caa", + "0x059405994b0e8b49eeccac51801f92d8adbc42fd48c9dd274f888e910168794e" ] [previous_kernel.vk] @@ -34,30 +34,30 @@ vk_path = [ "0x0000000000000000000000000000000000000000000000000000000000000008", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x00000000000000000000000000000047fcf1905cfb9c15a4cf70e2bcfd474d85", - "0x000000000000000000000000000000000024d65dcb0bd8a5858d952683a41b77", - "0x00000000000000000000000000000012ec744073c90ffacde9920ccdc9b2ec0c", - "0x00000000000000000000000000000000001589f6176d7c3b522e247839d03c3c", - "0x000000000000000000000000000000f2a1b1acf4189cd49f4a5621f649e70870", - "0x0000000000000000000000000000000000052ebbf58f8e639679198d151b9ed7", - "0x000000000000000000000000000000cf7e43a131550b37eaade38d7b0521b004", - "0x00000000000000000000000000000000002e036dc7f8d6d060d82040cd9d17be", - "0x000000000000000000000000000000852848d65f65174efa0fd5533a5f54e65d", - "0x00000000000000000000000000000000001df16fa41488410eb321659295e2e1", - "0x000000000000000000000000000000bdb6e51c038cb89f317934188b2959cdcf", - "0x000000000000000000000000000000000012b4a41976ae7bcbcb13e27cb62c13", - "0x000000000000000000000000000000f45eb660bbfeb050d079dd5693bf1942ae", - "0x000000000000000000000000000000000029d01e5ed21019197b196f53354af7", - "0x0000000000000000000000000000006191a44843f17c60653513a8462479c7ea", - "0x00000000000000000000000000000000002245e7005b195c0e7f3524024a87dc", - "0x0000000000000000000000000000004cda575acbeb954f0ee0ef8114ab658cfe", - "0x0000000000000000000000000000000000071a841a527e4215805092bb03036f", - "0x0000000000000000000000000000005a57318d58fa61e0c9b7d9f4c3a80f1b20", - "0x000000000000000000000000000000000004e664896b1a21bf4df731f29b5e44", - "0x00000000000000000000000000000083c78dce9ac7a4d3ffc740f892b01ece3e", - "0x000000000000000000000000000000000011a474b8cb53bcbd0a24dcf4aecc7f", - "0x0000000000000000000000000000009f919ddbe27224277b03efa993b9871e09", - "0x00000000000000000000000000000000000af8c7f638b0d1fd03695f158eb1f8", + "0x000000000000000000000000000000ebf5c5e0a11331dfdb990f9affda2c7acb", + "0x00000000000000000000000000000000002c2182d0325a1eb08e9868398f217e", + "0x000000000000000000000000000000568ae5c24713d23956f22c4e6c59e4ab35", + "0x00000000000000000000000000000000001db62722cf260493c5f0bf2acdf8f4", + "0x0000000000000000000000000000004ab9531e8a34f37ba1c175c382a0080f36", + "0x00000000000000000000000000000000000b3ba6701649728cf9476dda83a651", + "0x000000000000000000000000000000f120647a2ac2c671e097c55e7bbbb853f9", + "0x000000000000000000000000000000000015f8344e4e2a00924670744778b963", + "0x000000000000000000000000000000bf9f694862ec356256ff1c98cc28504721", + "0x000000000000000000000000000000000022a8a812fe097f92105dc400a4ba07", + "0x000000000000000000000000000000906579400862d21b1b659cd66a075e9a52", + "0x000000000000000000000000000000000027e53e3742e98c85534e831eea3708", + "0x0000000000000000000000000000006109ca104fe9ddc82b4bfcef95d9ac0bd6", + "0x00000000000000000000000000000000002ee76084579dd85dfbec7d0fa157b3", + "0x000000000000000000000000000000eccb1ec58c0a93e52676c447fabd921bff", + "0x000000000000000000000000000000000024b846cae630f4af85df863ed31cc9", + "0x000000000000000000000000000000eeb498751477887d5a70e05ae30a05862b", + "0x000000000000000000000000000000000012efe6f7100c43b4e7625966035ada", + "0x0000000000000000000000000000009c5cde9ebd4da47df4fd235372c18c43c2", + "0x000000000000000000000000000000000005729704e19543361e3eeab74a6d22", + "0x000000000000000000000000000000dd2506aa0ef813149a92f3b60c75666cbb", + "0x0000000000000000000000000000000000150c590e8a7e0374debfa0bad48954", + "0x000000000000000000000000000000682d4cf6bef57f313681db3cca5c1b4919", + "0x0000000000000000000000000000000000120146fe1b5f8bf295469abd6bace6", "0x000000000000000000000000000000cf3f0cf483e3b60ac46e2580628429291f", "0x0000000000000000000000000000000000179a988d2f894ba4cc456686236e49", "0x00000000000000000000000000000041c39c0b069ca7761686f7caf8bd51c343", @@ -66,14 +66,14 @@ vk_path = [ "0x0000000000000000000000000000000000298c3311fc9170f92de940b042aab9", "0x000000000000000000000000000000bf37537eb196b05c1d98fa51016a9bacbb", "0x000000000000000000000000000000000007b05f408a612847259016b9204ae4", - "0x000000000000000000000000000000f670252c93ae136d88595e191d4128b02a", - "0x00000000000000000000000000000000001c425066f305565a4c7cd5db7af8de", - "0x0000000000000000000000000000009088c10942bb491838d082350343bfba5f", - "0x000000000000000000000000000000000001a1083740b17399823e9dde69f9ab", - "0x000000000000000000000000000000a5be3bce0a3fa922de3e7c8e61540fbd94", - "0x00000000000000000000000000000000002b627b9dcf54b342237a640f0379f3", - "0x000000000000000000000000000000d9f6b4eec34dbfac3c49b2e9efbb60c04a", - "0x00000000000000000000000000000000000f3e556b4b04370af11386f427cba1", + "0x00000000000000000000000000000081636f7e563a7726b880d8186b979208b6", + "0x000000000000000000000000000000000022ec848dea89705ef497a6bf286ffa", + "0x00000000000000000000000000000014f2da48a725196c1dd911a13332e85dc9", + "0x00000000000000000000000000000000000225e1d754fda3ad8e98b71f19bbd6", + "0x00000000000000000000000000000055bf971c7cc3c871c193ce5859eee58df3", + "0x000000000000000000000000000000000022af239b05cb27daa47aac7f8ba9da", + "0x000000000000000000000000000000659e41800ac74f6706dd8d1eba5c8dff10", + "0x00000000000000000000000000000000001079d84d7045456e80f6a655bedfe5", "0x00000000000000000000000000000035c9cb5f001915ccb707544d8295b4e4f8", "0x00000000000000000000000000000000002451c090209c18e440624b96591a23", "0x000000000000000000000000000000381694f1ab41bc30edb347a4618bbcb253", @@ -82,46 +82,46 @@ vk_path = [ "0x0000000000000000000000000000000000172c855547ef1cf358b1193059ab94", "0x000000000000000000000000000000c746ba121a8412b23d1a54c2679b7fcb42", "0x0000000000000000000000000000000000188578f3213bb6dba81fc0826a77f1", - "0x00000000000000000000000000000017f1ad5a36d1d27be395a245b27103333d", - "0x000000000000000000000000000000000004aaea2a12bbe7658d9ebb39bcc49c", - "0x0000000000000000000000000000003d5674ba283399f8ff8e9a4a54ef04f90b", - "0x0000000000000000000000000000000000085ced78b3c248687e55753187ae1a", - "0x000000000000000000000000000000c3f69116b99bc6ffe2ea33348aedab107f", - "0x00000000000000000000000000000000002ac3afb3486a8008fe304176553b8f", - "0x0000000000000000000000000000002b2112dd48e799d1264079e8ef0d1bcaa9", - "0x00000000000000000000000000000000002c9560a53e0bded7eeda7efc3128ce", - "0x00000000000000000000000000000016078a51c3eba292cf5db78ca1a10302a0", - "0x00000000000000000000000000000000002b45bd8d7deeb00c5dd28950fe8663", - "0x000000000000000000000000000000c6b10731458f7591b2f2054fa35130d3d7", - "0x000000000000000000000000000000000021bc70d2c7a3e3778eb03692800f0b", - "0x0000000000000000000000000000002250c4d7f459e91ac8a90146ea1bd57b17", - "0x0000000000000000000000000000000000166328613b357e3f52b0a324ce9bbc", - "0x000000000000000000000000000000d6865b1299db5db396a4b6a83884374bcf", - "0x00000000000000000000000000000000001a294f3d8c5a65e3ffa26b30cd48fa", - "0x0000000000000000000000000000006a4397a48841d23ba00fbca0938310b285", - "0x00000000000000000000000000000000001d5f058d9f8d9ec1215ec97fa5301a", - "0x00000000000000000000000000000010f0a48520cc4a265730c3c253896592d9", - "0x00000000000000000000000000000000000c18a511c4a55a638137ab511d6b59", - "0x000000000000000000000000000000c26ef47b8d2648c3a6fe2e90d56acc1080", - "0x00000000000000000000000000000000000c2cfe2c3d862f0c3c783129b30f4a", - "0x00000000000000000000000000000078b499a64139eb4b9c3a35de09abd9f325", - "0x00000000000000000000000000000000001a97475ec4b37efa29c0672d0900bd", - "0x000000000000000000000000000000c7f3136538eda650068d285385c9377bf3", - "0x00000000000000000000000000000000002eea440707e0ae65d1619ec3a5164d", - "0x000000000000000000000000000000f10f12e5d7d6458c7378e61545241d48bc", - "0x000000000000000000000000000000000011d7f193dc021ba1f70a356d076b5b", - "0x000000000000000000000000000000b74efab19101de5bd968ebeae34d83671d", - "0x00000000000000000000000000000000001e2aa317e8b7bff29a3b77fc5c3899", - "0x0000000000000000000000000000007f89d35cce826817315ef179bb8c271021", - "0x000000000000000000000000000000000017793433109d3e74b4e23c99ff852a", - "0x00000000000000000000000000000037e7f362482e126278c998278d38361479", - "0x000000000000000000000000000000000003e0b3732d506db0c7d4cd2aa72059", - "0x000000000000000000000000000000064c3c62ab0bcad8ddc92d3f417b19103a", - "0x0000000000000000000000000000000000160df69c00c0175d5529c05f51fad1", - "0x000000000000000000000000000000aed90b6927439a7d2642ca7486fa7a7ada", - "0x00000000000000000000000000000000002730c29b29642172ff9349cd2a93d6", - "0x00000000000000000000000000000029ef0b340cfe7aebd25e9d138f3f152124", - "0x000000000000000000000000000000000009578e1e7edcc86fce5d6da67e403a", + "0x000000000000000000000000000000b8bfc0ec72460534646ca70473b769e469", + "0x0000000000000000000000000000000000049b8d0d775977aced41eb139190a1", + "0x00000000000000000000000000000096f3f7b6aadf2bd52f2924c7122667ce25", + "0x00000000000000000000000000000000000525a7e6f2f97879bcc820bf777253", + "0x000000000000000000000000000000f4d3cf67a283478a5eac6fbe17ee96eed2", + "0x00000000000000000000000000000000000ad1b561d9594259b882488d0cc327", + "0x000000000000000000000000000000a411ca082e39592f1fc853c2d513bbd5aa", + "0x000000000000000000000000000000000008d3f884d8438ee5215018dcaa188a", + "0x000000000000000000000000000000bf7be169a8de2908615313618db268f1e4", + "0x00000000000000000000000000000000001d9c9cfcf8a51a99044b9f0587f056", + "0x00000000000000000000000000000099779ccf55d880d2d89e29037f196ae1c2", + "0x000000000000000000000000000000000006568f730db5bd3bc244f173966590", + "0x000000000000000000000000000000c73c86792d680daaa16bc4abbfc86b36d6", + "0x000000000000000000000000000000000018a43d744283305357571bb152559c", + "0x000000000000000000000000000000ced414c54d769e60dcd3682ba4c44248f1", + "0x00000000000000000000000000000000002676417b4d3d996511fd014e6a23c6", + "0x0000000000000000000000000000008cfdd43b773f44db6718acd462a86d90af", + "0x000000000000000000000000000000000009c74f562726924ff211d4f7524e69", + "0x000000000000000000000000000000b09ea57eba399017aaa98b0eb547353994", + "0x0000000000000000000000000000000000036854ad7145a3b27cf15af9057ddd", + "0x00000000000000000000000000000083b536aae497f9d924ade23e7127810cc1", + "0x0000000000000000000000000000000000006d0e7a8c41c75a810e0f61c988ab", + "0x0000000000000000000000000000008b4dff72b0302530d56dcc085fef3b0659", + "0x00000000000000000000000000000000002d42030b25a2c9fa440273acd3d9b8", + "0x00000000000000000000000000000033fa6421f3efb6f7879e4bef32691b0541", + "0x000000000000000000000000000000000017303d410b8dbb71abfa387efcdca0", + "0x000000000000000000000000000000c863243719c89bed01d21aa0faa6c339f0", + "0x00000000000000000000000000000000001d4a177083592f36a544168ff6e32c", + "0x00000000000000000000000000000052bafeb15210b248e0c5b3f5fb04c5caab", + "0x000000000000000000000000000000000021912375ee7bcdefbe6d17b9fd66cc", + "0x00000000000000000000000000000055ef740ae612cb6188a042179d6c0b6b97", + "0x000000000000000000000000000000000022d7654a72f9437fed1815e6646998", + "0x000000000000000000000000000000765b9df0cbc6d8d347d01e317f6c893ff1", + "0x000000000000000000000000000000000018ff3e8da1e9c3881881570633ad5e", + "0x000000000000000000000000000000066d781507a8b9374f1251ec2802b1f3cf", + "0x000000000000000000000000000000000023cc8521f1819767eacaeec7706a3f", + "0x00000000000000000000000000000017a033cae1e76033c3974fccf3b4381d97", + "0x00000000000000000000000000000000000b0e562914d2007ead1c34e9643bd9", + "0x000000000000000000000000000000e4788505d1e560008b97ca8b69672c269d", + "0x0000000000000000000000000000000000155e1aa60f8a100ed7504ecb8661c7", "0x000000000000000000000000000000f6f4596202301b6ae4eb0ebbeadd203340", "0x00000000000000000000000000000000000adc89c48d75b571636f5bbeb4a806", "0x00000000000000000000000000000000034e3e27454ef992b4bf84b97baa7471", @@ -130,10 +130,10 @@ vk_path = [ "0x00000000000000000000000000000000002e25783551df50c004ec7cd1f4dd8b", "0x000000000000000000000000000000e8258f84477c1b62565a559ba7bb38832e", "0x000000000000000000000000000000000018f76cf0ceeccb4798de741ae89b64", - "0x0000000000000000000000000000001583b176f599e192d7119354034419e8f9", - "0x000000000000000000000000000000000004706a0e23ac32a3566907fb872362", - "0x000000000000000000000000000000d1b9992279342fce9a883849693fcda22a", - "0x000000000000000000000000000000000029046b299293cb09c593372eb6b3e6", + "0x000000000000000000000000000000353d43fa70e99239c1c1c67e271a0eeac5", + "0x00000000000000000000000000000000002d299fb68678d0150bcc5b16dc8252", + "0x0000000000000000000000000000002814ede7cd27daed00c33c12860bc4b046", + "0x000000000000000000000000000000000015d3ac5a199abb74933a4efc98c59b", "0x000000000000000000000000000000469680c270e551515344592f59188fa765", "0x00000000000000000000000000000000002d38d6d4ba1e4763a74ecdb11ca1f3", "0x000000000000000000000000000000fce917c0d5dca019477c52f6075332b612", @@ -142,10 +142,10 @@ vk_path = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000c77bfd4e323bf4b587a765f94fd95a8b79", - "0x000000000000000000000000000000000024009f6e07ac7a3b6be0bb489fbecc", - "0x00000000000000000000000000000057a8b3aca5f0c3ee69ebb7065f7ad49d24", - "0x00000000000000000000000000000000001cbb1e4420739c580a8ddde8b93cf6", + "0x0000000000000000000000000000002601343ed9ec702f7e521ebe0ffbd691f3", + "0x000000000000000000000000000000000012ff74941b932aa806c1bc5e1a178f", + "0x0000000000000000000000000000005cfadbe0d6f87ff27fb836c4f6cbb5f414", + "0x00000000000000000000000000000000000f4f5d92fbaa233a1b3681560577ec", "0x0000000000000000000000000000006241ca0c75be2d15e6b9188983d6b41e2d", "0x00000000000000000000000000000000001a71ab9767b295f9337074850d2d40", "0x000000000000000000000000000000ec7f970d2da2ca7f41836eb044f5a75b01", @@ -155,28 +155,28 @@ vk_path = [ "0x00000000000000000000000000000078f4b3bc61f19d6e7069359bbf47e7f907", "0x00000000000000000000000000000000002d7c18a93c3dae58809faaeec6a86a" ] - hash = "0x23a2a161bf6411223041b53e3dfc9b62249771587188adefd16695b0361456ce" + hash = "0x070d9b598d7d30b2bf554714f28264ff0060c35eead5024d392a322d9d3a4ad9" [previous_kernel_public_inputs] min_revertible_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000003" is_private_only = true -claimed_first_nullifier = "0x281fee20d8354147ce54e1186a5312d51ef0d7bf7a6f40f61ed5e150948b79c0" +claimed_first_nullifier = "0x0a2d604709f8fe9ce3bf2edd952b13699b46e8621f9374ffecff8a646d183cee" [previous_kernel_public_inputs.constants] - vk_tree_root = "0x130ae70545763dadb59caeb820400cffc41df59bcc2b8731917644f247717686" - protocol_contract_tree_root = "0x2f9edcbdd0cfc67764aa32cdef9ea44f2914b44bfd9cf77d518a5ac172e7f9e4" + vk_tree_root = "0x215f80cda67ebdb1bca149b200664f9f453218edcd6d74840eefbcbdb653086f" + protocol_contract_tree_root = "0x0765c36c56d790ae7929d668acf0f5c38cc5e898c893b538bd784a40d7018098" [previous_kernel_public_inputs.constants.historical_header] - total_fees = "0x0000000000000000000000000000000000000000000000000003a83222de8a00" + total_fees = "0x0000000000000000000000000000000000000000000000000003a831c6a84280" total_mana_used = "0x0000000000000000000000000000000000000000000000000000000000004a40" [previous_kernel_public_inputs.constants.historical_header.last_archive] - root = "0x082bf9a6382021ca9fa204bdbc71fd5536d84358e57a61f129fdf5bfb228c06f" + root = "0x16608d96b4901a721581dc02e113b68871e027b09c54811bba56924a4494f9ce" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000008" [previous_kernel_public_inputs.constants.historical_header.content_commitment] num_txs = "0x0000000000000000000000000000000000000000000000000000000000000001" - blobs_hash = "0x00310bb50f2202183669b8a85c09986fd69c203fee7d32e06dd1b46c84b20a7d" + blobs_hash = "0x00d3ca5f4aa587c294535143deef5eaf88278e8780cd3f0e7c577bd5e40e2450" in_hash = "0x00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c" out_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -185,33 +185,33 @@ root = "0x2e33ee2008411c04b99c24b313513d097a0d21a5040b6193d1f978b8226892d6" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [previous_kernel_public_inputs.constants.historical_header.state.partial.note_hash_tree] -root = "0x2596f3d6b05b7055c3c5ab89de6e270b653cb30c3dd6b9cfd230ff41b5ffa623" +root = "0x2a44514b808f61b25e71d1f2c3f65aefd446d41d9a9a36ec89e709be5b82b598" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000240" [previous_kernel_public_inputs.constants.historical_header.state.partial.nullifier_tree] -root = "0x15205da8ec6fc0317cee4482de04084768078d73397e7f466c11c45c2cb66bac" +root = "0x0867e7af1fb7ccdd91be072954eca54426bfbcd46a83afc96a71ac930a76d165" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000002c0" [previous_kernel_public_inputs.constants.historical_header.state.partial.public_data_tree] -root = "0x2841d0ae135bd8bf3761f919c16b9e5cf7ad4b3035328f65bf84ee2dc599aed6" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000097" +root = "0x1b1e369f0479fa96c779683d2cc01b3002931d28e48717c5d16450dfb8003bf2" +next_available_leaf_index = "0x000000000000000000000000000000000000000000000000000000000000009a" [previous_kernel_public_inputs.constants.historical_header.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" version = "0x0000000000000000000000000000000000000000000000000000000000000001" block_number = "0x0000000000000000000000000000000000000000000000000000000000000008" slot_number = "0x0000000000000000000000000000000000000000000000000000000000000022" - timestamp = "0x00000000000000000000000000000000000000000000000000000000678ac01a" + timestamp = "0x0000000000000000000000000000000000000000000000000000000067a3b983" [previous_kernel_public_inputs.constants.historical_header.global_variables.coinbase] - inner = "0x00000000000000000000000017ad47b9f14dc201975cca47fea765e5d42f0b72" + inner = "0x00000000000000000000000071de811f9b5b8d82122993e4f401c4776c6d8029" [previous_kernel_public_inputs.constants.historical_header.global_variables.fee_recipient] inner = "0x0000000000000000000000000000000000000000000000000000000000000000" [previous_kernel_public_inputs.constants.historical_header.global_variables.gas_fees] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" - fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd3a5e8" + fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd267fa" [previous_kernel_public_inputs.constants.tx_context] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" @@ -227,23 +227,23 @@ l2_gas = "0x00000000000000000000000000000000000000000000000000000000005b8d80" [previous_kernel_public_inputs.constants.tx_context.gas_settings.max_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" -fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd0959c" +fee_per_l2_gas = "0x00000000000000000000000000000000000000000000000000000012e9bb9bf7" [previous_kernel_public_inputs.constants.tx_context.gas_settings.max_priority_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" [previous_kernel_public_inputs.validation_requests.for_rollup.max_block_number._opt] -_is_some = false -_value = "0x0000000000000000000000000000000000000000000000000000000000000000" +_is_some = true +_value = "0x0000000000000000000000000000000000000000000000000000000000000012" [[previous_kernel_public_inputs.validation_requests.note_hash_read_requests]] [previous_kernel_public_inputs.validation_requests.note_hash_read_requests.read_request] -value = "0x0493504d7e2628c6f4c13f91240ac2aeff1175f772039af764ebced0da4326e4" +value = "0x2f3068fb5d1037dfe562bd7425700f2637cd0a6c708bc0068d9af4e6313f542e" counter = "0x0000000000000000000000000000000000000000000000000000000000000002" [previous_kernel_public_inputs.validation_requests.note_hash_read_requests.contract_address] -inner = "0x238db6644a299b7b33993c163a0b3f3f3aaeca32f9d0f9d0328c1615d7efb8a5" +inner = "0x1ec8af141dd61668d55b47def5c69e3b97742187cfa911c55deb13ff8c4b4390" [[previous_kernel_public_inputs.validation_requests.note_hash_read_requests]] [previous_kernel_public_inputs.validation_requests.note_hash_read_requests.read_request] @@ -2739,7 +2739,7 @@ inner = "0x0000000000000000000000000000000000000000000000000000000000000000" [[previous_kernel_public_inputs.end.nullifiers]] [previous_kernel_public_inputs.end.nullifiers.nullifier] -value = "0x281fee20d8354147ce54e1186a5312d51ef0d7bf7a6f40f61ed5e150948b79c0" +value = "0x0a2d604709f8fe9ce3bf2edd952b13699b46e8621f9374ffecff8a646d183cee" counter = "0x0000000000000000000000000000000000000000000000000000000000000000" note_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -4883,7 +4883,7 @@ counter = "0x0000000000000000000000000000000000000000000000000000000000000000" inner = "0x0000000000000000000000000000000000000000000000000000000000000000" [[previous_kernel_public_inputs.end.private_call_stack]] -args_hash = "0x166e357b48d9e320bc83c169c41e4beb5635fa0adf7634bf88e326caf1d6500c" +args_hash = "0x09ff7556b7f1f8747a7cff52e9d4d9a424f9c23170b61d233163121aa1f808c0" returns_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" start_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000003" end_side_effect_counter = "0x000000000000000000000000000000000000000000000000000000000000000c" @@ -4892,13 +4892,13 @@ end_side_effect_counter = "0x000000000000000000000000000000000000000000000000000 is_static_call = false [previous_kernel_public_inputs.end.private_call_stack.call_context.msg_sender] - inner = "0x238db6644a299b7b33993c163a0b3f3f3aaeca32f9d0f9d0328c1615d7efb8a5" + inner = "0x1ec8af141dd61668d55b47def5c69e3b97742187cfa911c55deb13ff8c4b4390" [previous_kernel_public_inputs.end.private_call_stack.call_context.contract_address] - inner = "0x1f3f85b45ea6c7be79b6e4948002182167732a202d83e1dba67b53f676583c83" + inner = "0x28da25b414c15e3780cd14c55a77ee681255341ae6ba104c8ea9b4dc621c6e54" [previous_kernel_public_inputs.end.private_call_stack.call_context.function_selector] - inner = "0x000000000000000000000000000000000000000000000000000000009462d279" + inner = "0x000000000000000000000000000000000000000000000000000000000c508454" [[previous_kernel_public_inputs.end.private_call_stack]] args_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -5042,18 +5042,8 @@ end_side_effect_counter = "0x000000000000000000000000000000000000000000000000000 [previous_kernel_public_inputs.fee_payer] inner = "0x0000000000000000000000000000000000000000000000000000000000000000" -[private_call] -contract_class_artifact_hash = "0x15414f6e022d2a3762d8c41f5a6e75b7f3e26efe3ba13e6600c7f89e048a5ad6" -contract_class_public_bytecode_commitment = "0x304f7153a18c819c6c02dfeb305a78fd590e90f6025cd590c247f90550c6fa88" -protocol_contract_sibling_path = [ - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000" -] -acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" - - [private_call.vk] - key = [ +[private_call.vk] +key = [ "0x0000000000000000000000000000000000000000000000000000000000100000", "0x0000000000000000000000000000000000000000000000000000000000000010", "0x00000000000000000000000000000000000000000000000000000000000328b1", @@ -5077,30 +5067,30 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000003bdf7db1ff2537303be867170cd4f20949", - "0x00000000000000000000000000000000000e249c643ba2cc9ce795e6a48c9014", - "0x000000000000000000000000000000933343a82c3246a8c6eedd7d4848da5b3a", - "0x000000000000000000000000000000000019eb16c922d67c482bb3675011f001", - "0x0000000000000000000000000000009bc0e6e0876d1e77c66c1e0dac0ac406cb", - "0x000000000000000000000000000000000006001c1053b3d2f54571a3d120c6e8", - "0x000000000000000000000000000000f925cdc9eeb2a03bcfae68ba87d419a64e", - "0x00000000000000000000000000000000001a17bc96707562869ede7b03eab1ee", - "0x00000000000000000000000000000074a721ef95c5c96e50c80bb4510583766c", - "0x00000000000000000000000000000000002ae7f9faf5fc385ae8adac3ecb4d38", - "0x0000000000000000000000000000006a47b624e274bab5652785eb8f7484341f", - "0x00000000000000000000000000000000002a7a550eec650f34dfda3cc3db02a4", - "0x000000000000000000000000000000913a686c5615f6cb8af07d2a77c8615235", - "0x000000000000000000000000000000000007c76949ec83c3137d67296f72cd48", - "0x000000000000000000000000000000ea2349c07364d4e7887d07ff5eb2391daa", - "0x0000000000000000000000000000000000088fd727a67a4993308b04d3f11522", - "0x0000000000000000000000000000001384c0f821f019503c4fc5f1fb2118f5d6", - "0x00000000000000000000000000000000001786c4b865cfc9c4405712e13e695c", - "0x00000000000000000000000000000037f48c686d003ee7c1665b2b28bf8affef", - "0x000000000000000000000000000000000023b7a1f2b950a2717a9fbe02a0ed22", - "0x000000000000000000000000000000b871965291f4912fd8526f10fe4a317052", - "0x0000000000000000000000000000000000200b4c60a6ca7471776ffd99f0abe3", - "0x00000000000000000000000000000046a17d69acd4e67fbe993aeeb6706bf751", - "0x000000000000000000000000000000000024b83c337969a1e73f0e9d5e0e7c55", + "0x000000000000000000000000000000ba17c246b3c6daca90edb0c9ce41c111da", + "0x000000000000000000000000000000000008cee1e4bf6a75a00437d71577496b", + "0x000000000000000000000000000000f09480d76662107c123881d6b90fb512a2", + "0x000000000000000000000000000000000003fbd9cbd151ae4101fc070fc18e9d", + "0x0000000000000000000000000000000304eccc6b1a39c66cdf1fa0a59f22448e", + "0x000000000000000000000000000000000011bf15ce7adbc3b63127ac28664011", + "0x00000000000000000000000000000012e5d2e1a8dc5ac7dff8215dd13a5e2709", + "0x00000000000000000000000000000000002f28afcb501fd28f4abd1bbb1444eb", + "0x000000000000000000000000000000d3d5c80d5dcb852524cc5ffd828ab1ed63", + "0x000000000000000000000000000000000014cd140237dc4b1f933f278c5d92db", + "0x000000000000000000000000000000601e3021acb959cc4d9b5dde91cc70b543", + "0x00000000000000000000000000000000001c64290aadfaac3a0d277cfdd8c4c0", + "0x000000000000000000000000000000155c05b4114764261c6f7d2059b28357a0", + "0x0000000000000000000000000000000000138870f9d38994a12e44e43ce580a4", + "0x000000000000000000000000000000c72db81b6ca89afa144d921df87de656dc", + "0x000000000000000000000000000000000013c4546a63b009286df117ce76f2a0", + "0x000000000000000000000000000000a32b8e8b55f7d95c945f9118a7fac61b68", + "0x0000000000000000000000000000000000161441f405c774e696eabe792a7586", + "0x00000000000000000000000000000038d3d6cb581fe64ae791587b602a4faa73", + "0x0000000000000000000000000000000000292e29ef496f338bed5ff6dcbaa065", + "0x000000000000000000000000000000350e6ad9be45ffdd7b78c4fc9c034604d5", + "0x00000000000000000000000000000000000d80d89c7f09837129f90bf7dc0895", + "0x000000000000000000000000000000b3580f0ad362117183fb5a59ece9f2bb10", + "0x00000000000000000000000000000000001e92536aa2e57ef09a922e091fcbc5", "0x000000000000000000000000000000552393d09533e1b2004bd84e9f8a53a88f", "0x00000000000000000000000000000000000456bb701b5b0b463bbcf0c4fe7190", "0x00000000000000000000000000000074156481e9a3f8f091d30db1003a6daab4", @@ -5109,62 +5099,62 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x0000000000000000000000000000000000298c3311fc9170f92de940b042aab9", "0x000000000000000000000000000000bf37537eb196b05c1d98fa51016a9bacbb", "0x000000000000000000000000000000000007b05f408a612847259016b9204ae4", - "0x000000000000000000000000000000bf7fa9c277c8b0222f5741a5bf95ab65fa", - "0x0000000000000000000000000000000000057554fc0b3da260f08fe4d1dc736e", - "0x000000000000000000000000000000659ebab7bc9c53a3ae317a50ffb57326ec", - "0x0000000000000000000000000000000000083d9fb556aff1dc091afcbc4e3e87", - "0x000000000000000000000000000000027b52191c8aca7a5be9b82c40ef4fc75a", - "0x00000000000000000000000000000000001abeeca613b18d4232b379345a81e0", - "0x00000000000000000000000000000089b585c8587baadcbcb83d7b9ddf0c475a", - "0x000000000000000000000000000000000004d0bf2c341ef5362969078cb4c087", - "0x00000000000000000000000000000051a87cef6fc1fc139c638070eeb6332e56", - "0x00000000000000000000000000000000002f54bb0062b7d1188f56014b559e12", - "0x0000000000000000000000000000008b54df63e7e28a33d41f9da53348df041e", - "0x000000000000000000000000000000000012e92e36a0c7a2bcdf5fc5556a4359", - "0x00000000000000000000000000000094cbf780f4134b698bf7f5b1fcdcad4218", - "0x0000000000000000000000000000000000113ca8d354541fa79cad64fcedbc88", - "0x000000000000000000000000000000df5a0e52dee5b07510fef30dc58ecacf15", - "0x000000000000000000000000000000000006c8fa942e2eec5728e8ff33fff291", - "0x0000000000000000000000000000001a11657db53c31cd6a8fcec994a3158927", - "0x00000000000000000000000000000000002613389312f17bf8200d7417eaf411", - "0x000000000000000000000000000000c15d6ac79bcca289a93da1a9ac6fdb27ea", - "0x000000000000000000000000000000000012e0eb64e8ecd9386e72ff809830cf", - "0x000000000000000000000000000000c99eaa58fe0c7882af7a4362d48b1f38ae", - "0x000000000000000000000000000000000007b22301f910a411020601992dd510", - "0x0000000000000000000000000000005a902f70f203e3f78a3d386e76063e9342", - "0x00000000000000000000000000000000002c5386267d0dc48fde623519d73f6f", - "0x000000000000000000000000000000a8b8826ddefdec361c493b85e80af5e818", - "0x000000000000000000000000000000000010a01f3102e35e8dfe664efdc09c9e", - "0x000000000000000000000000000000efd8f11cd513ffb019426cdebcbe0c0a02", - "0x000000000000000000000000000000000009401fc4a3743c8417f2735f7b1a14", - "0x000000000000000000000000000000857c369073bf2d97f6e80d1df5bf336ad5", - "0x00000000000000000000000000000000000d479d048221d823f196f0cf6e462b", - "0x0000000000000000000000000000007b07b5028dbfe2a9ca4af2867fa142a3ea", - "0x0000000000000000000000000000000000041f7963140cf6900ddf687bb85e52", - "0x0000000000000000000000000000005a160094e4af7ff1564fd8830634bdf888", - "0x00000000000000000000000000000000000eb846b83b2ac3275a04e3e0e28203", - "0x0000000000000000000000000000004e60a98a3b99be5bde12cecf3a56b65c61", - "0x0000000000000000000000000000000000077acc807b84e48d68f261b3ecfb1e", - "0x0000000000000000000000000000001e937a7281020a557d906e9ac9f221a17a", - "0x0000000000000000000000000000000000291f84227889d040b7a6e739c890ed", - "0x00000000000000000000000000000053c118a34b4851610d80e50083b98272e5", - "0x000000000000000000000000000000000013ecdb14d48a8852d009fb403bc1ca", - "0x0000000000000000000000000000003cabfec95eed026483a697f8790150af7f", - "0x00000000000000000000000000000000001f4374d8e970c4898226e6e58feb74", - "0x0000000000000000000000000000000541e71410607a56d161f6b6c96cce96d7", - "0x00000000000000000000000000000000001c4881d7a7db6937a443981d951910", - "0x0000000000000000000000000000005060492f10be18b4455cb07a8b31ac7456", - "0x0000000000000000000000000000000000054763cb2b5fd4fbe3fac8526365f1", - "0x000000000000000000000000000000e67471001887b1154781b45f00083eab69", - "0x00000000000000000000000000000000001241c629327270322eddf420334224", - "0x000000000000000000000000000000ea0f153b38032c5b76433c2a0b8e38bc8f", - "0x00000000000000000000000000000000001e6d72c8d8ebc142e892a8eda4a52a", - "0x000000000000000000000000000000c0817f2462bd4fb3224670822255612822", - "0x00000000000000000000000000000000001b27509da5b256524a89816419bac9", - "0x0000000000000000000000000000004a235a960857ae76c2fd5da68e619b06ae", - "0x00000000000000000000000000000000001c76a05a30fbf6653a46db5fa4c467", - "0x0000000000000000000000000000005eaaa79d85433cb15a3ac41b49a4df2383", - "0x000000000000000000000000000000000020472c312cba098038a94fdfc52fbd", + "0x000000000000000000000000000000939bf1a5b7809d8b01f3644d25320c1057", + "0x00000000000000000000000000000000000bb9c327c5c46150d6a9f881da3f97", + "0x000000000000000000000000000000373deb6abda4cbd0aeb599f5e2cbc8c7d0", + "0x000000000000000000000000000000000008853658bb9a976b9c5759f6e4a7a7", + "0x000000000000000000000000000000c0dde24b40843332cba16e5819b70252e8", + "0x000000000000000000000000000000000028d8801d55f465e503ea406c01aba5", + "0x000000000000000000000000000000948b3a2c03a15b1cefaf61a304e4a719da", + "0x000000000000000000000000000000000003b7cfd2b82a92094655eebf43f587", + "0x00000000000000000000000000000027e9ad698de14515bc8d1b6cae9fbad57f", + "0x00000000000000000000000000000000001c0a8b31ac9b62bed2b663095c7c6d", + "0x00000000000000000000000000000032be17a5c7ec6f478466d2a20658a26691", + "0x00000000000000000000000000000000000ad876deed75f59e602b4310acca72", + "0x000000000000000000000000000000823ca734148f958a26e328b3f7742ea48c", + "0x00000000000000000000000000000000000327674919840515cc9adc69a08fca", + "0x0000000000000000000000000000003b11524c9317fca9485e116204c763b8b4", + "0x00000000000000000000000000000000001bfe8c3873c37bf972dcc81e0f31f9", + "0x0000000000000000000000000000002ac5cca6118d139ec7f25a9b76a5ddcafe", + "0x0000000000000000000000000000000000234c4d5c9182f86728981254d9b542", + "0x00000000000000000000000000000073e9bec3d89f0a089185e5c495abc03f49", + "0x00000000000000000000000000000000000e8e66e8280d22daafda145be28759", + "0x000000000000000000000000000000fdf638672b651ed51efc005c104462f0e6", + "0x0000000000000000000000000000000000080352ea8b7367481b2706ade1e8dd", + "0x0000000000000000000000000000000fb762794564a807d2fe4f1796aeb91374", + "0x00000000000000000000000000000000002fb18b04275219efb2d8b9993ac5a4", + "0x000000000000000000000000000000de088cc8c0527b80157b779e36b5584684", + "0x00000000000000000000000000000000002b2479dba4b8536cad4536a76772eb", + "0x00000000000000000000000000000088f079ceae5b612632d0f90ff9870eb518", + "0x000000000000000000000000000000000012dc0d37f799f65987161df01f2725", + "0x00000000000000000000000000000035dbe3c0ad6adec423daf4f4d006fc2107", + "0x000000000000000000000000000000000013be267471ce48c6fc419ed2c4f448", + "0x00000000000000000000000000000058f3c8b2d3fa8d27c7e9d00a30ab43cc10", + "0x0000000000000000000000000000000000013c74cd0d5d7b5b5d5c9efe463700", + "0x000000000000000000000000000000cc57f7a6fd16721223c06423fe824d808b", + "0x00000000000000000000000000000000002b7ed029a6fd2bb3aea57c910f4ce5", + "0x000000000000000000000000000000e160b523de9603e839cde7f13fa3b33f01", + "0x000000000000000000000000000000000026d602349ce611d3a0a13e99ff39f1", + "0x0000000000000000000000000000009a937ef599676d703f16e420f8ff58bf67", + "0x00000000000000000000000000000000000953bb1e7b32d830c5287273593692", + "0x000000000000000000000000000000ec2a55268e5541e16ef75936b7ef712da4", + "0x00000000000000000000000000000000002fb62718859b835874d34b239520a6", + "0x000000000000000000000000000000f0e554bf0a57474980d6587c90240108df", + "0x00000000000000000000000000000000001b59c63da545cf00a79032128aad3b", + "0x000000000000000000000000000000a96e68b12d0a7e2b6f8ce543e2bb429461", + "0x00000000000000000000000000000000000a7bc231143365bea08a875a15ae48", + "0x00000000000000000000000000000041faf11f15be8c711e2d6788919ca8a678", + "0x00000000000000000000000000000000001765e8b8b608510cac4df5441fee72", + "0x00000000000000000000000000000080166245f4c87e53148ccee1e5b9df2e64", + "0x000000000000000000000000000000000009580fbf9ea8ea3f770c1319f80570", + "0x000000000000000000000000000000a29e551e64e61881d2ab207e14e541fa97", + "0x00000000000000000000000000000000002e5d31bfdd37c018b0b3044580e4b3", + "0x0000000000000000000000000000007bf40e2a38bcfcea5c03729bb156a3e737", + "0x00000000000000000000000000000000000d5cb0eea228961fe7367c81bea0f7", + "0x0000000000000000000000000000007b6209c1a43ffce20c3fac25735bf423bc", + "0x00000000000000000000000000000000000d03c063cb3753b1fd740dc307ef61", + "0x00000000000000000000000000000067acda10350e2f21b2cf23eb60638577ae", + "0x00000000000000000000000000000000000324b34c98af03b9a5a00231efa4b8", "0x000000000000000000000000000000f6f4596202301b6ae4eb0ebbeadd203340", "0x00000000000000000000000000000000000adc89c48d75b571636f5bbeb4a806", "0x00000000000000000000000000000000034e3e27454ef992b4bf84b97baa7471", @@ -5173,10 +5163,10 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x00000000000000000000000000000000002e25783551df50c004ec7cd1f4dd8b", "0x000000000000000000000000000000e8258f84477c1b62565a559ba7bb38832e", "0x000000000000000000000000000000000018f76cf0ceeccb4798de741ae89b64", - "0x0000000000000000000000000000001583b176f599e192d7119354034419e8f9", - "0x000000000000000000000000000000000004706a0e23ac32a3566907fb872362", - "0x000000000000000000000000000000d1b9992279342fce9a883849693fcda22a", - "0x000000000000000000000000000000000029046b299293cb09c593372eb6b3e6", + "0x000000000000000000000000000000353d43fa70e99239c1c1c67e271a0eeac5", + "0x00000000000000000000000000000000002d299fb68678d0150bcc5b16dc8252", + "0x0000000000000000000000000000002814ede7cd27daed00c33c12860bc4b046", + "0x000000000000000000000000000000000015d3ac5a199abb74933a4efc98c59b", "0x000000000000000000000000000000469680c270e551515344592f59188fa765", "0x00000000000000000000000000000000002d38d6d4ba1e4763a74ecdb11ca1f3", "0x000000000000000000000000000000fce917c0d5dca019477c52f6075332b612", @@ -5185,10 +5175,10 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x00000000000000000000000000000075f2056228f93bf0d873a60a684060bac7", - "0x00000000000000000000000000000000000ed6a9d48ed84a9718a64975641bc6", - "0x0000000000000000000000000000009698bf13611dd2109f6494f3e1a33c6383", - "0x0000000000000000000000000000000000116b3f49fd12a2708f94254c90529e", + "0x000000000000000000000000000000ba6eceb4a88fd5005334c5194c00506c85", + "0x00000000000000000000000000000000002fcc8b2bd2f3a85f1929b70c55add2", + "0x000000000000000000000000000000603adb125ea1222b105d309b4e151b3f67", + "0x000000000000000000000000000000000016acc112d291730189e913ba819edf", "0x000000000000000000000000000000b5c1eac95b264c302dc854e6f22d7330df", "0x00000000000000000000000000000000000fcbbf9d3cf402baa3eeda5f0a9e49", "0x000000000000000000000000000000def9d58fc2920836194261f7b163fefbaf", @@ -5198,43 +5188,112 @@ acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" "0x00000000000000000000000000000078f4b3bc61f19d6e7069359bbf47e7f907", "0x00000000000000000000000000000000002d7c18a93c3dae58809faaeec6a86a" ] - hash = "0x0357b1c540f5af4835a03e641e58f894fd8cae860097f0525cb52fb35ca59f75" +hash = "0x1f136008f15076069fefca2d3d5aef5cf09266b34ac5a0d5aa1f2bb3db3d5f83" - [private_call.function_leaf_membership_witness] - leaf_index = "6" +[private_call.verification_key_hints] +contract_class_artifact_hash = "0x2afacaaea7c332e3c765b32d69e4e0b98c1a89151372048ff76aa250ff302645" +contract_class_public_bytecode_commitment = "0x1d991be1124ab8654b93432ca1c6345e2dd272d018b57f9355b90e75b8e691ba" +protocol_contract_sibling_path = [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" +] +acir_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +updated_class_id_value_change = [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" +] +updated_class_id_delay_change = [ + "0x0000000000000000000000000000000000000000000000000000000000000000" +] + + [private_call.verification_key_hints.function_leaf_membership_witness] + leaf_index = "1" sibling_path = [ - "0x1a9373cc06d328dbb65d4da16d226ed8537ae2bc7c72840f10906095000e7541", - "0x17fa0fe6dbc546b83cd4c651d2b13a4fed404f198edff542128745b162413615", - "0x11763591ea4405c8e7d6c73b334fe4bab9e00287a58522b8bd7424e46e73e7fe", - "0x182358ea0fc90d11dbd0cd2d57a39099441f17abd2b7c084f99af71f51f80c83", + "0x1ad6f5048722d3b1d70eb9fc3134002fd74631a10f404b3b88a11fbea98b8c81", + "0x1cd3f5bd4a2737ce75dc0909b13078f90df57ba65c204ad9dec43a7d453a4946", + "0x0775cab0d259e9759af58a730f46a8edc0ef0d8c5cb42a7e591893f79b2574bf", + "0x3012fd3ea84e29e5ab0c6c4ddc1a5654faeb5ff9a4970babc8e01f48cbb4862a", "0x2a6595890719fef7967c209488728aa5342438ba52058a3c770202f55acf6854" ] -[private_call.public_keys.npk_m.inner] +[private_call.verification_key_hints.public_keys.npk_m.inner] x = "0x01498945581e0eb9f8427ad6021184c700ef091d570892c437d12c7d90364bbd" y = "0x170ae506787c5c43d6ca9255d571c10fa9ffa9d141666e290c347c5c9ab7e344" is_infinite = false -[private_call.public_keys.ivpk_m.inner] +[private_call.verification_key_hints.public_keys.ivpk_m.inner] x = "0x00c044b05b6ca83b9c2dbae79cc1135155956a64e136819136e9947fe5e5866c" y = "0x1c1f0ca244c7cd46b682552bff8ae77dea40b966a71de076ec3b7678f2bdb151" is_infinite = false -[private_call.public_keys.ovpk_m.inner] +[private_call.verification_key_hints.public_keys.ovpk_m.inner] x = "0x1b00316144359e9a3ec8e49c1cdb7eeb0cedd190dfd9dc90eea5115aa779e287" y = "0x080ffc74d7a8b0bccb88ac11f45874172f3847eb8b92654aaa58a3d2b8dc7833" is_infinite = false -[private_call.public_keys.tpk_m.inner] +[private_call.verification_key_hints.public_keys.tpk_m.inner] x = "0x019c111f36ad3fc1d9b7a7a14344314d2864b94f030594cd67f753ef774a1efb" y = "0x2039907fe37f08d10739255141bb066c506a12f7d1e8dfec21abc58494705b6f" is_infinite = false - [private_call.salted_initialization_hash] - inner = "0x298ab83b1d9ebb322d2a8acebe7c4ed7be5cacabc4e96cbee49db1962a0ab3ae" + [private_call.verification_key_hints.salted_initialization_hash] + inner = "0x01a48c4ac9036876efb58e6d2ccbe01f3d4026dd9831a11b15c9950ec05df1ba" + + [private_call.verification_key_hints.updated_class_id_witness] + leaf_index = "146" + sibling_path = [ + "0x2f64a8f5ffc6aa56ebc2a35e599736586416d249555f0e94d5d62398eeba5c95", + "0x25c335a21e4880d1111d64509af6c26118bc8373899216ec2b49214eb08e1733", + "0x1630f40cf7d179178f2c9620ecec29c82259e400053d6076ada737bd342bbeb2", + "0x19315696be2ca3ad03243b7562e52467e3960a5596990f0d0766bcf1218b66fc", + "0x00e67ba426c1d84d9b734f5ffb63978cc4809364e587ce009283b879c396105f", + "0x120157cfaaa49ce3da30f8b47879114977c24b266d58b0ac18b325d878aafddf", + "0x01c28fe1059ae0237b72334700697bdf465e03df03986fe05200cadeda66bd76", + "0x21874f891782d18ffd9fa527f87b05eca98ef8eeafa2d21e91f0c1f59b8e31f9", + "0x067243231eddf4222f3911defbba7705aff06ed45960b27f6f91319196ef97e1", + "0x1849b85f3c693693e732dfc4577217acc18295193bede09ce8b97ad910310972", + "0x2a775ea761d20435b31fa2c33ff07663e24542ffb9e7b293dfce3042eb104686", + "0x0f320b0703439a8114f81593de99cd0b8f3b9bf854601abb5b2ea0e8a3dda4a7", + "0x0d07f6e7a8a0e9199d6d92801fff867002ff5b4808962f9da2ba5ce1bdd26a73", + "0x1c4954081e324939350febc2b918a293ebcdaead01be95ec02fcbe8d2c1635d1", + "0x0197f2171ef99c2d053ee1fb5ff5ab288d56b9b41b4716c9214a4d97facc4c4a", + "0x2b9cdd484c5ba1e4d6efcc3f18734b5ac4c4a0b9102e2aeb48521a661d3feee9", + "0x14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3", + "0x071d7627ae3b2eabda8a810227bf04206370ac78dbf6c372380182dbd3711fe3", + "0x2fdc08d9fe075ac58cb8c00f98697861a13b3ab6f9d41a4e768f75e477475bf5", + "0x20165fe405652104dceaeeca92950aa5adc571b8cafe192878cba58ff1be49c5", + "0x1c8c3ca0b3a3d75850fcd4dc7bf1e3445cd0cfff3ca510630fd90b47e8a24755", + "0x1f0c1a8fb16b0d2ac9a146d7ae20d8d179695a92a79ed66fc45d9da4532459b3", + "0x038146ec5a2573e1c30d2fb32c66c8440f426fbd108082df41c7bebd1d521c30", + "0x17d3d12b17fe762de4b835b2180b012e808816a7f2ff69ecb9d65188235d8fd4", + "0x0e1a6b7d63a6e5a9e54e8f391dd4e9d49cdfedcbc87f02cd34d4641d2eb30491", + "0x09244eec34977ff795fc41036996ce974136377f521ac8eb9e04642d204783d2", + "0x1646d6f544ec36df9dc41f778a7ef1690a53c730b501471b6acd202194a7e8e9", + "0x064769603ba3f6c41f664d266ecb9a3a0f6567cd3e48b40f34d4894ee4c361b3", + "0x1595bb3cd19f84619dc2e368175a88d8627a7439eda9397202cdb1167531fd3f", + "0x2a529be462b81ca30265b558763b1498289c9d88277ab14f0838cb1fce4b472c", + "0x0c08da612363088ad0bbc78abd233e8ace4c05a56fdabdd5e5e9b05e428bdaee", + "0x14748d0241710ef47f54b931ac5a58082b1d56b0f0c30d55fb71a6e8c9a6be14", + "0x0b59baa35b9dc267744f0ccb4e3b0255c1fc512460d91130c6bc19fb2668568d", + "0x2c45bb0c3d5bc1dc98e0baef09ff46d18c1a451e724f41c2b675549bb5c80e59", + "0x121468e6710bf1ffec6d0f26743afe6f88ef55dab40b83ca0a39bc44b196374c", + "0x2042c32c823a7440ceb6c342f9125f1fe426b02c527cd8fb28c85d02b705e759", + "0x0d582c10ff8115413aa5b70564fdd2f3cefe1f33a1e43a47bc495081e91e73e5", + "0x0f55a0d491a9da093eb999fa0dffaf904620cbc78d07e63c6f795c5c7512b523", + "0x21849764e1aa64b83a69e39d27eedaec2a8f97066e5ddb74634ffdb11388dd9a", + "0x2e33ee2008411c04b99c24b313513d097a0d21a5040b6193d1f978b8226892d6" +] + + [private_call.verification_key_hints.updated_class_id_leaf] + slot = "0x201f7703728762b9c55cdc1d8438429b2212ac6852f647d7e3bacdfa25add655" + value = "0x00007212d2e47049672ac4c925cad339853b3c847c64dcb44f9de9a8c8543021" + next_slot = "0x23b7ce153ad5ff3a10648b3830d3032863825f835af06b86c2ef45f69814a568" + next_index = "0x0000000000000000000000000000000000000000000000000000000000000098" [app_public_inputs] -args_hash = "0x166e357b48d9e320bc83c169c41e4beb5635fa0adf7634bf88e326caf1d6500c" +args_hash = "0x09ff7556b7f1f8747a7cff52e9d4d9a424f9c23170b61d233163121aa1f808c0" returns_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" start_side_effect_counter = "0x0000000000000000000000000000000000000000000000000000000000000003" end_side_effect_counter = "0x000000000000000000000000000000000000000000000000000000000000000c" @@ -5249,16 +5308,16 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" is_static_call = false [app_public_inputs.call_context.msg_sender] - inner = "0x238db6644a299b7b33993c163a0b3f3f3aaeca32f9d0f9d0328c1615d7efb8a5" + inner = "0x1ec8af141dd61668d55b47def5c69e3b97742187cfa911c55deb13ff8c4b4390" [app_public_inputs.call_context.contract_address] - inner = "0x1f3f85b45ea6c7be79b6e4948002182167732a202d83e1dba67b53f676583c83" + inner = "0x28da25b414c15e3780cd14c55a77ee681255341ae6ba104c8ea9b4dc621c6e54" [app_public_inputs.call_context.function_selector] - inner = "0x000000000000000000000000000000000000000000000000000000009462d279" + inner = "0x000000000000000000000000000000000000000000000000000000000c508454" [[app_public_inputs.note_hash_read_requests]] - value = "0x28fbb2cacdbe2324a0235f00f1ec191464ffb328d57e20c900cfb6bc12fd319a" + value = "0x0747dfbdbcb284408f206cc6845a79cb4b07f8f84c6c1d7fcc32585215486046" counter = "0x0000000000000000000000000000000000000000000000000000000000000005" [[app_public_inputs.note_hash_read_requests]] @@ -5322,7 +5381,7 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" counter = "0x0000000000000000000000000000000000000000000000000000000000000000" [[app_public_inputs.nullifier_read_requests]] - value = "0x1f3f85b45ea6c7be79b6e4948002182167732a202d83e1dba67b53f676583c83" + value = "0x28da25b414c15e3780cd14c55a77ee681255341ae6ba104c8ea9b4dc621c6e54" counter = "0x0000000000000000000000000000000000000000000000000000000000000004" [[app_public_inputs.nullifier_read_requests]] @@ -5389,11 +5448,11 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" sk_app_generator = "0x0000000000000000000000000000000000000000000000000000000000000030" [app_public_inputs.key_validation_requests_and_generators.request] - sk_app = "0x15be083e9b2c85b0a8cc2a0712dcc938823ab5f6b869847c1e994435632a38b6" + sk_app = "0x0b094aeeb5da415069f829108669acf756979de8efc044ee9124459fbe94a566" [app_public_inputs.key_validation_requests_and_generators.request.pk_m] - x = "0x2bcd3866b5cfda1e4ec60ac07d18d963d3dc0312565f0bd87c57bee8b11a3d79" - y = "0x2a1698b41b5275c81f388c8197320a7f2c3ea6e00eff1b6fc766d37438813d91" + x = "0x1f1226734a3242043d1a672b8da63965ef24fd07760c199affb79df17b639297" + y = "0x0c56ef273702885eac2aeff918d1002e1bd3a7f76f5309b378899bf4be901fed" is_infinite = false [[app_public_inputs.key_validation_requests_and_generators]] @@ -5562,11 +5621,11 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" is_infinite = false [[app_public_inputs.note_hashes]] - value = "0x2cafbfeb3e7c909004b904102aac2a9582d346f0f6ada422f486492196051233" + value = "0x168bb17da350a76255491d6cbfb4a65efb762a7176b27128306ea1142ce4a3be" counter = "0x0000000000000000000000000000000000000000000000000000000000000007" [[app_public_inputs.note_hashes]] - value = "0x04c273cbaf5afd1382819de56f33c8fb6e960bc09176f37a5dec40d502f12c62" + value = "0x1b2afd7ccec005e51ac4e9ea94033edbed9e2bf6ac68ed86133c2c454a13a53b" counter = "0x0000000000000000000000000000000000000000000000000000000000000009" [[app_public_inputs.note_hashes]] @@ -5626,7 +5685,7 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" counter = "0x0000000000000000000000000000000000000000000000000000000000000000" [[app_public_inputs.nullifiers]] - value = "0x122ee8c7874eabb5cf1f69f00db2d410b655f0bcc53afa708021706364afd7ef" + value = "0x2f17a536ab7acaf3213848d090c4583e809b09884b4e2a607438815ffa1ec9a1" counter = "0x0000000000000000000000000000000000000000000000000000000000000006" note_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -6084,24 +6143,24 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.private_logs.log] fields = [ - "0x2e32a1bf9721ea7ac582e3b35e774ab60094ecc3fc2c5fbf0dcf5f7d6cee6698", - "0x0082cacdc0883affca92b2dcdb9af956f6192ef05474bc86076801abe666822d", - "0x00d1801f9babbb3bf5292c117d047dc797e5550d804a89c52184ffd0a935f86e", - "0x00b60ac0e97815ae9a3408cf73bd6adc29f81600000000000000000000000000", - "0x000000e356dd076944646a7d0d9b399c4729cbb98d26a8ffbe6e7c8c501f2963", - "0x00ee59e34e8f118e4708c1528c76cd147fabab02e60e4d2d217913e10f00cf2e", - "0x00cf8d899c4944ba06dcfc39316959288f7395416c20085969bf8e588bcd1fd7", - "0x00924f2f0452218bebfdf2b5c0444b4a0ef9f9a660f9aaee507e17cedded6fd2", - "0x0097264a024e3cfb8cceaef83f8f397ecca3289e2e3e9d00107bc4e2481eb661", - "0x003768fe926d5020575afa09cb17699ccc253f08d1e1a7ac978a04ba6b97f4b9", - "0x00b8e731bbd83bd780754094b6c9938ee6d0cc04dee604813bd725b2977d1bea", - "0x000c5f6307e12ee0744767333e0ca0648b4e2cf6650adf7153237e1087ceee46", - "0x00e20263558e8d451cd095bfa601a75224117229863974b73d4f5426474922cc", - "0x00f3e0caf71bf7f2aa33b92208a05524126aff183f9294c54ef463122672fcba", - "0x0096accd0b9908f8369cc1bc28fc54a759ca8d1ce0af0654513a7ecc1d50d271", - "0x00b69328094a64c3552d1728db429bcd73340cfa9459c6ce1ad6c56166a37dcd", - "0x00830ef479a9e5cc4e1b75f5589a0e2417f3a4cbc68ef1b370c4156b6d9a5f74", - "0x005bd7224a7f39b063c6e992ebe154e12281a01aebeffafd16d8577580e17d69" + "0x13e3d78ed3c2279751b799128aa60f7a4129247e678a044a7a8a88311368ca59", + "0x22486ce340d8ad1ce98cdb7fcc1969c805ababeb7eca028b5cf100d032e434ce", + "0x000149798f5fb666b6cce9c47f9ed10703677746d2dde70415121dddae13d161", + "0x0069ec04e6a2f2ad7ac7847d10fbf26a3acad0d8cd8fbbc3df982ad406c0d121", + "0x00af2b60318795239c492398acd0ebf5b9a3a8961766d5d427630e4f77720edf", + "0x00f5a07adca5983382dd70755f8a83f7b2a800b18e829a23305bf57bcc3c63ae", + "0x00c295074009981fb034237002882d8ac490fde854412668d1be3dd50670614f", + "0x00d38d7b87bc08f2392a9615644d7095db609ad438e9015dabc898d3019bba3f", + "0x00089e6a2648c8e6cb73af053e5d713cff7a70fe8a612afada9ba03bafacde97", + "0x006b6a4c23affcc00d5c13e85f1b225e0906e1d61654be37ab52d924a83e3947", + "0x019a6838a5e1cdfb7dd823bd9e5969f3f22a8013bfca2b9b6b1893d1799961a4", + "0x2cd2f3e7a0cc4e076c384bce2781bb97d7b805de0670c1b9127bef2eafe7b17c", + "0x049258c3ec28d2098dbd547e1044853e050b170c4960a2a00d584631c3ab9779", + "0x040484aa96bcdfd7e34ab8d9dbd64fdde38e763e0b65641002e7035119f854ae", + "0x1ee033513411d0147323a132923bec74456fc2ccc5693dab166817b1d26fbf20", + "0x2beb16a97535b64b3192d789ab7250c5cc413838e6015c02cbaa425df7e11442", + "0x0f4753f1818533fa2cad78440fd35ea1bf608d33547a33432ef93bb07d1bdf55", + "0x187991df5cbbc87f437baa678fdb94111cb37de60401b096d2aecf6b3b18afa3" ] [[app_public_inputs.private_logs]] @@ -6110,24 +6169,24 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.private_logs.log] fields = [ - "0x0a570547eb3932bde8012946db288e43d15b2b6201c4a8c24dcdc31e74317f07", - "0x00986014aa0a16f4050f9b72f252ad67e1ab7e54aefdd50024de0fbca090b7bd", - "0x00b83ab21999d102e579e42acd2ccb20eab1908950d6699b3055de76ddd747df", - "0x00f024650d7a651cea573bf424bca297e10d7800000000000000000000000000", - "0x0000002a523111425aa81aaadb0b44bf69b379981d2533f9c612c4e9a6fa5deb", - "0x009c9d8bc4a68e4492251e9ca642e5a2e5cd9a160a7f57e5e46cd98e8a505808", - "0x00de7309e6a91b41088b1a47e9552b8e451b6f43e96a5673218c634ab1c40133", - "0x007613a8fec9d559e9dafbf39b03a32a7ee26a4b25f25d9b9b91f1ce51282e0e", - "0x00161afa20c5fdeea686e623050f648706ff62faac4db9f29c3c93d118aaa8bf", - "0x00fcc00ff46b7169a3248e9db2abfe2eac468189ce75d99969e7e8a790975de9", - "0x00c37cf06c06b68337a440ce015dc36820269afc7c457b8ec73434fe68deb5c6", - "0x00ab73a1669860e535c58239484d1ef16a4212f773b247d244f4d5dbaa7a24d8", - "0x00e68f2b1ce5b4cd53193c1a530f771cc5c59763cbf5fd8f58d0d57403ebccd0", - "0x00ebf20948171df2bfecad0de722795ddc75b15937329af62e07e12e9e3114e6", - "0x00b5960ae83493a99bfbcad07b07e591604c5c78c0ea780089aaab639cdd677d", - "0x00e6e800c329d9ce3f246e256a1fe5fffe39728ec323130d530f40ced22148e4", - "0x00a62c81f757682c09e637fb6649a7ad1e8efc8ff594f4adaabca94ef67f9e21", - "0x005dfd4efd18389adce33c361be54c715d285c1022408a988813aa59ec2111df" + "0x09ed050c5ff8d1233daf1772e9ac2b59013cfae76ae5ca98f4d41ccbde71b890", + "0x20a0892164635dca88e2bffdff283013dc72f9d228bdd8f5baa17d765f5d5127", + "0x0001c65374b6e980afbc4487459ca7f4dd71dc34ac04cf65f241e6c2927ec193", + "0x0046a7e83cc80e2e77c7f74a085d48f9c5297d9e4f23de072e435c2788c8b69a", + "0x004df9b835e2c7a219df9fe66813250a9382e58718471a6d5531ff20e187aae3", + "0x00b1feaf5cfe48678d25858010bfa60f7afb76acc6405b2a380a4dc54c93a917", + "0x0036a8b28bb22e05b1eccbd261fef16519289e19895bfaaa5e404a0c6e870c33", + "0x002eeee188efcc2328f59f966c2ce3e8e7abad77bd1e08e45a9659e34b6a70f1", + "0x000ae9136884874bc8869d62cd32c21dc8cd75f317f4ec59d9588b54d6a895fb", + "0x00542aa3d42a6011372a59bcfe8852a167c1585a58dce2347ad98ad85ad5960e", + "0x0abd460f768b6d494ef7a0f27cfd96d8f23d629468016bfa294f24271b502fc9", + "0x23a62456c8bb6f509988702cd9bca2c8a6b9da325ed5a34fd41646c50ce25e44", + "0x0e33cd84a38daebe7859d0d9323a3f3db66a8c667c97885e0b91d23db50ddf52", + "0x1d7497ef2215afb94080e141553f719abdfe118fe7c62758f9adef24cd0c7f26", + "0x03350a4b60c4750c49322c8de03451eedd2d2bce13960fd5f5f848eddd54fbd8", + "0x23c37cd2bd560b4b81046048858ad99c7313cca3ce190ced250fbb2233959c31", + "0x24e95ecd1c49fc24951b249e3c0adfc9cecdacf433d0bc04da2ad666cf74a58b", + "0x28aebc959c07df78b6cb86ce6750e0fa76191fdcfc4d2555b26fa72c9099517c" ] [[app_public_inputs.private_logs]] @@ -6136,24 +6195,24 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.private_logs.log] fields = [ - "0x120392bb7b0f8a8074677a0ec94e240a1acee0d9907cd821664152e60c93ded7", - "0x000a6c12926cca2912dbb8ff8fda8181b0243de560be01d928228b9b3f1a3eaa", - "0x002382c13dba0643828b98521b34734036d3bfc97066aa008c6ccb06888be3f9", - "0x00611e40f1e790fe462bd800e2ba4f335997c000000000000000000000000000", - "0x000000410f6d6eafd70ac039270f357c36b0beb38cd7ef63c986a435473d0ed0", - "0x0080e268092c9b5762fa283d78988a06fc2f9c1aa35d5c10b0b4280028488f28", - "0x0089ae8feb56e222b42652d8e7a97aacff3a140a98c61cc4044698943665e1e2", - "0x002a2cb08a5146da9f6caddc991e623123a007f0603889dc0e212a94fd801cdc", - "0x0045cff3d9fd6cf35c993c420d998713e19a8c007decddec14f21c32fb5afc8b", - "0x000182adbb07469facd7de4ba531dc0ed7d5db96d5084de633dead278bab965e", - "0x00c63292d155b9d917e885f2b414d83f91c360334d6b22872b52ea1c023f4ee4", - "0x00534cffc48b9572de519f01c60f8e5e1f56007259230a461334d69767f92918", - "0x0087e035599cff2dc997de9f2dacd0f3e124834cd8af2f1e8aea643c58f04022", - "0x00cb91a242d997379360dfa4a194fdfa6e6055034c39978f5d569228b0f3059b", - "0x007ce93f5450d7bc0a8993a7a74fe1818e2dacce5c85c0dc453c63143a0f75ac", - "0x00c90b6bcb9ab012e79c6d3e847d1c1240f0be071e79aadb45da9af31831f945", - "0x0081b31045ee003463012d3ad5e35d8337ea5dd43ed60a77c8d1acdb6bd60446", - "0x004654374e9340c5024063ff6109dce7ea7bd88a834317d1e0a1a91435c13d0f" + "0x13a54ec4402861105096f0393323312a09976ae019222445121c04054ac9ec6a", + "0x17eb3877093ea497ac5e38573b2db7148366c8c82dba7182f467b382796231e6", + "0x000173782c41e067d9a0feb64a8f3d1905c4bcf4798efe53548c8fc3d511cfc4", + "0x00cbf5f8d5e393ba5e9a4a7706b71fbe9963329bdf90dbeb9a98cb9db610f039", + "0x00f77c2c7f5a07845f22df61780333cd64100b1943f88124e7998653c4414ca5", + "0x006fb008842f5579bbb7fe598c74ce390b3be8629a1e79da1b06952d294efb42", + "0x00aff2fcdc3ecd1f427712d74da3f4ceaf702ee9bf3f7d7d0475b47b83a4fb29", + "0x00608b14da6637ca4949024e087054acccd0afa4e68f3521a4e606f340d3a05a", + "0x008e54bb42bc5975765988702f5e2da1b4a0ad0bc581783a4afbde974b93a95e", + "0x00d196717cd4d65f049104e5e0c893ad92f899715187b7dcd698d337d549b349", + "0x01d11cea4e47b6f770627d2c4405bdda46539bf8aa2a3c8f64444418cfd1eb1c", + "0x098ff3799db62cc1e503a490fa1d85017f0341b0ab83b4a4d144836ef5a76caa", + "0x25b9cb6367a805ad55c831ede863eccba785f7d36c79935c13ead26fd8474be3", + "0x266e34214676e0c61c2e1ba5a34d80a137f5d00de636231c0556d270638db9fe", + "0x11aaf22e8af1a473eff86f98acaaee471c9816d7538cdf39daf6c1fe4405d0b1", + "0x205ddea56d7f060958d3f7041fd72cd719c889f28824888ec307ea6c21b0119a", + "0x0aed46ceb6c0a8cdb65934202e0e85b5952dd147dc949fc0feee9b8c0e135c16", + "0x2675e16f545c0f312b926ac831aa2a7f7e955239ca0988498c2069db262b56c0" ] [[app_public_inputs.private_logs]] @@ -6500,16 +6559,16 @@ _value = "0x0000000000000000000000000000000000000000000000000000000000000000" length = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.historical_header] - total_fees = "0x0000000000000000000000000000000000000000000000000003a83222de8a00" + total_fees = "0x0000000000000000000000000000000000000000000000000003a831c6a84280" total_mana_used = "0x0000000000000000000000000000000000000000000000000000000000004a40" [app_public_inputs.historical_header.last_archive] - root = "0x082bf9a6382021ca9fa204bdbc71fd5536d84358e57a61f129fdf5bfb228c06f" + root = "0x16608d96b4901a721581dc02e113b68871e027b09c54811bba56924a4494f9ce" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000008" [app_public_inputs.historical_header.content_commitment] num_txs = "0x0000000000000000000000000000000000000000000000000000000000000001" - blobs_hash = "0x00310bb50f2202183669b8a85c09986fd69c203fee7d32e06dd1b46c84b20a7d" + blobs_hash = "0x00d3ca5f4aa587c294535143deef5eaf88278e8780cd3f0e7c577bd5e40e2450" in_hash = "0x00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c" out_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" @@ -6518,33 +6577,33 @@ root = "0x2e33ee2008411c04b99c24b313513d097a0d21a5040b6193d1f978b8226892d6" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [app_public_inputs.historical_header.state.partial.note_hash_tree] -root = "0x2596f3d6b05b7055c3c5ab89de6e270b653cb30c3dd6b9cfd230ff41b5ffa623" +root = "0x2a44514b808f61b25e71d1f2c3f65aefd446d41d9a9a36ec89e709be5b82b598" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000240" [app_public_inputs.historical_header.state.partial.nullifier_tree] -root = "0x15205da8ec6fc0317cee4482de04084768078d73397e7f466c11c45c2cb66bac" +root = "0x0867e7af1fb7ccdd91be072954eca54426bfbcd46a83afc96a71ac930a76d165" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000002c0" [app_public_inputs.historical_header.state.partial.public_data_tree] -root = "0x2841d0ae135bd8bf3761f919c16b9e5cf7ad4b3035328f65bf84ee2dc599aed6" -next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000097" +root = "0x1b1e369f0479fa96c779683d2cc01b3002931d28e48717c5d16450dfb8003bf2" +next_available_leaf_index = "0x000000000000000000000000000000000000000000000000000000000000009a" [app_public_inputs.historical_header.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" version = "0x0000000000000000000000000000000000000000000000000000000000000001" block_number = "0x0000000000000000000000000000000000000000000000000000000000000008" slot_number = "0x0000000000000000000000000000000000000000000000000000000000000022" - timestamp = "0x00000000000000000000000000000000000000000000000000000000678ac01a" + timestamp = "0x0000000000000000000000000000000000000000000000000000000067a3b983" [app_public_inputs.historical_header.global_variables.coinbase] - inner = "0x00000000000000000000000017ad47b9f14dc201975cca47fea765e5d42f0b72" + inner = "0x00000000000000000000000071de811f9b5b8d82122993e4f401c4776c6d8029" [app_public_inputs.historical_header.global_variables.fee_recipient] inner = "0x0000000000000000000000000000000000000000000000000000000000000000" [app_public_inputs.historical_header.global_variables.gas_fees] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" - fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd3a5e8" + fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd267fa" [app_public_inputs.tx_context] chain_id = "0x0000000000000000000000000000000000000000000000000000000000007a69" @@ -6560,7 +6619,7 @@ l2_gas = "0x00000000000000000000000000000000000000000000000000000000005b8d80" [app_public_inputs.tx_context.gas_settings.max_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" -fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000c9bd0959c" +fee_per_l2_gas = "0x00000000000000000000000000000000000000000000000000000012e9bb9bf7" [app_public_inputs.tx_context.gas_settings.max_priority_fees_per_gas] fee_per_da_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr index fe4fd6cc5f2..e9f3c5bd307 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_call_data_validator/validate_contract_address.nr @@ -1,6 +1,19 @@ use dep::types::{ - abis::private_kernel::private_call_data::PrivateCallData, address::AztecAddress, - constants::MAX_PROTOCOL_CONTRACTS, merkle_tree::root::root_from_sibling_path, + abis::private_kernel::private_call_data::PrivateCallData, + address::AztecAddress, + constants::{ + DEFAULT_UPDATE_DELAY, DEPLOYER_CONTRACT_ADDRESS, MAX_PROTOCOL_CONTRACTS, + UPDATED_CLASS_IDS_SLOT, + }, + contract_class_id::ContractClassId, + hash::private_functions_root_from_siblings, + merkle_tree::root::root_from_sibling_path, + shared_mutable::{ + scheduled_delay_change::ScheduledDelayChange, scheduled_value_change::ScheduledValueChange, + validate_shared_mutable_hints, + }, + storage::map::derive_storage_slot_in_map, + traits::{is_empty, Packable, ToField}, }; pub fn validate_contract_address( @@ -12,18 +25,28 @@ pub fn validate_contract_address( private_call_data.vk.check_hash(); - let computed_address = AztecAddress::compute_from_private_function( + let hints = private_call_data.verification_key_hints; + + let private_functions_root = private_functions_root_from_siblings( private_call_data.public_inputs.call_context.function_selector, private_call_data.vk.hash, - private_call_data.function_leaf_membership_witness, - private_call_data.contract_class_artifact_hash, - private_call_data.contract_class_public_bytecode_commitment, - private_call_data.salted_initialization_hash, - private_call_data.public_keys, + hints.function_leaf_membership_witness.leaf_index, + hints.function_leaf_membership_witness.sibling_path, ); - let protocol_contract_index = contract_address.to_field(); + let contract_class_id = ContractClassId::compute( + hints.contract_class_artifact_hash, + private_functions_root, + hints.contract_class_public_bytecode_commitment, + ); + let computed_address = AztecAddress::compute_from_class_id( + contract_class_id, + hints.salted_initialization_hash, + hints.public_keys, + ); + + let protocol_contract_index = contract_address.to_field(); let computed_protocol_contract_tree_root = if (MAX_PROTOCOL_CONTRACTS as Field).lt( protocol_contract_index, ) { @@ -32,13 +55,43 @@ pub fn validate_contract_address( root_from_sibling_path( computed_address.to_field(), protocol_contract_index, - private_call_data.protocol_contract_sibling_path, + private_call_data.verification_key_hints.protocol_contract_sibling_path, ) }; - assert( - computed_address.eq(contract_address) - | computed_protocol_contract_tree_root.eq(protocol_contract_tree_root), - "computed contract address does not match expected one", + let value_change: ScheduledValueChange = + Packable::unpack(hints.updated_class_id_value_change); + let delay_change: ScheduledDelayChange = + Packable::unpack(hints.updated_class_id_delay_change); + + // A block horizon for this shared mutable should be set separately when generating/validating kernel output + validate_shared_mutable_hints( + private_call_data.public_inputs.historical_header, + derive_storage_slot_in_map(UPDATED_CLASS_IDS_SLOT as Field, contract_address), + DEPLOYER_CONTRACT_ADDRESS, + value_change, + delay_change, + hints.updated_class_id_witness, + hints.updated_class_id_leaf, + ); + + let updated_contract_class_id = value_change.get_current_at( + private_call_data.public_inputs.historical_header.global_variables.block_number as u32, ); + + if is_empty(updated_contract_class_id) { + // No update happened, so we should check that the computed address matches the expected one + assert( + computed_address.eq(contract_address) + | computed_protocol_contract_tree_root.eq(protocol_contract_tree_root), + "computed contract address does not match expected one", + ); + } else { + // Update happened, we must be using the updated class id + assert( + contract_class_id.eq(updated_contract_class_id), + "updated contract not using latest class id", + ); + } } + diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_output_validator.nr index 2300ee88497..4f321bdf2d2 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_output_validator.nr @@ -8,9 +8,16 @@ use dep::types::{ private_circuit_public_inputs::{ PrivateCircuitPublicInputs, PrivateCircuitPublicInputsArrayLengths, }, + private_kernel::private_call_data::PrivateCallData, }, address::AztecAddress, - traits::is_empty, + constants::DEFAULT_UPDATE_DELAY, + contract_class_id::ContractClassId, + shared_mutable::{ + compute_shared_mutable_block_horizon, scheduled_delay_change::ScheduledDelayChange, + scheduled_value_change::ScheduledValueChange, + }, + traits::{is_empty, Packable}, transaction::tx_request::TxRequest, utils::arrays::{ assert_array_appended, assert_array_appended_and_scoped, assert_array_appended_reversed, @@ -30,7 +37,7 @@ impl PrivateKernelCircuitOutputValidator { pub fn validate_as_first_call( self, tx_request: TxRequest, - private_call: PrivateCircuitPublicInputs, + private_call: PrivateCallData, private_call_array_lengths: PrivateCircuitPublicInputsArrayLengths, vk_tree_root: Field, protocol_contract_tree_root: Field, @@ -50,7 +57,7 @@ impl PrivateKernelCircuitOutputValidator { offsets.nullifiers = 1; // The protocol nullifier is not propagated from the private call. } self.validate_propagated_from_private_call( - private_call, + private_call.public_inputs, private_call_array_lengths, offsets, 0, // num_popped_call @@ -61,7 +68,7 @@ impl PrivateKernelCircuitOutputValidator { self, previous_kernel: PrivateKernelCircuitPublicInputs, previous_kernel_array_lengths: PrivateKernelCircuitPublicInputsArrayLengths, - private_call: PrivateCircuitPublicInputs, + private_call: PrivateCallData, private_call_array_lengths: PrivateCircuitPublicInputsArrayLengths, ) { self.validate_aggregated_values(previous_kernel, private_call); @@ -70,7 +77,7 @@ impl PrivateKernelCircuitOutputValidator { previous_kernel_array_lengths, ); self.validate_propagated_from_private_call( - private_call, + private_call.public_inputs, private_call_array_lengths, previous_kernel_array_lengths, 1, // num_popped_call @@ -80,7 +87,7 @@ impl PrivateKernelCircuitOutputValidator { fn validate_initial_values( self, tx_request: TxRequest, - private_call: PrivateCircuitPublicInputs, + private_call: PrivateCallData, vk_tree_root: Field, protocol_contract_tree_root: Field, is_private_only: bool, @@ -91,7 +98,7 @@ impl PrivateKernelCircuitOutputValidator { assert_eq(self.output.constants.tx_context, tx_request.tx_context, "mismatch tx_context"); assert_eq( self.output.constants.historical_header, - private_call.historical_header, + private_call.public_inputs.historical_header, "mismatch historical_header", ); assert_eq(self.output.constants.vk_tree_root, vk_tree_root, "mismatch vk_tree_root"); @@ -115,21 +122,27 @@ impl PrivateKernelCircuitOutputValidator { // Others. assert_eq( self.output.min_revertible_side_effect_counter, - private_call.min_revertible_side_effect_counter, + private_call.public_inputs.min_revertible_side_effect_counter, "incorrect initial min_revertible_side_effect_counter", ); + + let max_block_number = Self::update_max_block_number_for_contract_updates( + private_call, + private_call.public_inputs.max_block_number, + ); + assert_eq( self.output.validation_requests.for_rollup.max_block_number, - private_call.max_block_number, + max_block_number, "incorrect initial max_block_number", ); assert_eq( self.output.public_teardown_call_request, - private_call.public_teardown_call_request, + private_call.public_inputs.public_teardown_call_request, "incorrect initial public_teardown_call_request", ); - let initial_fee_payer = if private_call.is_fee_payer { - private_call.call_context.contract_address + let initial_fee_payer = if private_call.public_inputs.is_fee_payer { + private_call.public_inputs.call_context.contract_address } else { AztecAddress::zero() }; @@ -139,19 +152,19 @@ impl PrivateKernelCircuitOutputValidator { fn validate_aggregated_values( self, previous_kernel: PrivateKernelCircuitPublicInputs, - private_call: PrivateCircuitPublicInputs, + private_call: PrivateCallData, ) { // min_revertible_side_effect_counter let propagated_min_revertible_counter = if previous_kernel .min_revertible_side_effect_counter != 0 { assert( - private_call.min_revertible_side_effect_counter == 0, + private_call.public_inputs.min_revertible_side_effect_counter == 0, "cannot overwrite min_revertible_side_effect_counter", ); previous_kernel.min_revertible_side_effect_counter } else { - private_call.min_revertible_side_effect_counter + private_call.public_inputs.min_revertible_side_effect_counter }; assert_eq( self.output.min_revertible_side_effect_counter, @@ -162,8 +175,12 @@ impl PrivateKernelCircuitOutputValidator { // max_block_number let max_block_number = MaxBlockNumber::min( previous_kernel.validation_requests.for_rollup.max_block_number, - private_call.max_block_number, + private_call.public_inputs.max_block_number, ); + + let max_block_number = + Self::update_max_block_number_for_contract_updates(private_call, max_block_number); + assert_eq( self.output.validation_requests.for_rollup.max_block_number, max_block_number, @@ -175,12 +192,12 @@ impl PrivateKernelCircuitOutputValidator { previous_kernel.public_teardown_call_request, ) { assert( - is_empty(private_call.public_teardown_call_request), + is_empty(private_call.public_inputs.public_teardown_call_request), "cannot overwrite public_teardown_call_request", ); previous_kernel.public_teardown_call_request } else { - private_call.public_teardown_call_request + private_call.public_inputs.public_teardown_call_request }; assert_eq( self.output.public_teardown_call_request, @@ -190,10 +207,10 @@ impl PrivateKernelCircuitOutputValidator { // fee_payer let propagated_fee_payer = if !is_empty(previous_kernel.fee_payer) { - assert(!private_call.is_fee_payer, "cannot overwrite fee_payer"); + assert(!private_call.public_inputs.is_fee_payer, "cannot overwrite fee_payer"); previous_kernel.fee_payer - } else if private_call.is_fee_payer { - private_call.call_context.contract_address + } else if private_call.public_inputs.is_fee_payer { + private_call.public_inputs.call_context.contract_address } else { AztecAddress::zero() }; @@ -354,4 +371,27 @@ impl PrivateKernelCircuitOutputValidator { offsets.private_call_stack - num_popped_call, ); } + + fn update_max_block_number_for_contract_updates( + private_call: PrivateCallData, + max_block_number: MaxBlockNumber, + ) -> MaxBlockNumber { + if !private_call.public_inputs.call_context.contract_address.is_protocol_contract() { + MaxBlockNumber::min( + max_block_number, + MaxBlockNumber::new(compute_shared_mutable_block_horizon( + ScheduledValueChange::::unpack( + private_call.verification_key_hints.updated_class_id_value_change, + ), + ScheduledDelayChange::::unpack( + private_call.verification_key_hints.updated_class_id_delay_change, + ), + private_call.public_inputs.historical_header.global_variables.block_number + as u32, + )), + ) + } else { + max_block_number + } + } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr index 695cecf6b40..6d611d73096 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/private_kernel_circuit_public_inputs_composer.nr @@ -6,11 +6,19 @@ use dep::types::{ max_block_number::MaxBlockNumber, nullifier::{Nullifier, ScopedNullifier}, private_circuit_public_inputs::PrivateCircuitPublicInputs, - side_effect::Ordered, + private_kernel::private_call_data::PrivateCallData, + side_effect::{Ordered, OrderedValue}, tx_constant_data::TxConstantData, }, address::AztecAddress, - traits::{Empty, is_empty}, + constants::DEFAULT_UPDATE_DELAY, + contract_class_id::ContractClassId, + debug::no_op, + shared_mutable::{ + compute_shared_mutable_block_horizon, scheduled_delay_change::ScheduledDelayChange, + scheduled_value_change::ScheduledValueChange, + }, + traits::{Empty, Hash, is_empty, Packable}, transaction::tx_request::TxRequest, utils::arrays::{array_length, array_to_bounded_vec, sort_by_counter_asc, sort_by_counter_desc}, }; @@ -120,11 +128,8 @@ impl PrivateKernelCircuitPublicInputsComposer { *self } - pub fn with_private_call( - &mut self, - private_call_public_inputs: PrivateCircuitPublicInputs, - ) -> Self { - self.propagate_from_private_call(private_call_public_inputs); + pub fn with_private_call(&mut self, private_call: PrivateCallData) -> Self { + self.propagate_from_private_call(private_call); *self } @@ -146,72 +151,97 @@ impl PrivateKernelCircuitPublicInputsComposer { self.public_inputs.finish() } - fn propagate_from_private_call(&mut self, private_call: PrivateCircuitPublicInputs) { + fn propagate_from_private_call(&mut self, private_call: PrivateCallData) { self.propagate_max_block_number(private_call); - self.propagate_note_hash_read_requests(private_call); - self.propagate_nullifier_read_requests(private_call); - self.propagate_key_validation_requests(private_call); - self.propagate_note_hashes(private_call); - self.propagate_nullifiers(private_call); - self.propagate_l2_to_l1_messages(private_call); - self.propagate_logs(private_call); - self.propagate_private_call_requests(private_call); - self.propagate_public_call_requests(private_call); - self.propagate_public_teardown_call_request(private_call); - self.propagate_fee_payer(private_call); - self.propagate_min_revertible_side_effect_counter(private_call); + self.propagate_note_hash_read_requests(private_call.public_inputs); + self.propagate_nullifier_read_requests(private_call.public_inputs); + self.propagate_key_validation_requests(private_call.public_inputs); + self.propagate_note_hashes(private_call.public_inputs); + self.propagate_nullifiers(private_call.public_inputs); + self.propagate_l2_to_l1_messages(private_call.public_inputs); + self.propagate_logs(private_call.public_inputs); + self.propagate_private_call_requests(private_call.public_inputs); + self.propagate_public_call_requests(private_call.public_inputs); + self.propagate_public_teardown_call_request(private_call.public_inputs); + self.propagate_fee_payer(private_call.public_inputs); + self.propagate_min_revertible_side_effect_counter(private_call.public_inputs); } fn propagate_min_revertible_side_effect_counter( &mut self, - private_call: PrivateCircuitPublicInputs, + private_call_public_inputs: PrivateCircuitPublicInputs, ) { if self.public_inputs.min_revertible_side_effect_counter != 0 { assert( - private_call.min_revertible_side_effect_counter == 0, + private_call_public_inputs.min_revertible_side_effect_counter == 0, "cannot overwrite non-zero min_revertible_side_effect_counter", ); } else { self.public_inputs.min_revertible_side_effect_counter = - private_call.min_revertible_side_effect_counter; + private_call_public_inputs.min_revertible_side_effect_counter; }; } - fn propagate_max_block_number(&mut self, private_call: PrivateCircuitPublicInputs) { + fn propagate_max_block_number(&mut self, private_call: PrivateCallData) { // Update the max block number if the private call requested a lower one. self.public_inputs.validation_requests.max_block_number = MaxBlockNumber::min( self.public_inputs.validation_requests.max_block_number, - private_call.max_block_number, + private_call.public_inputs.max_block_number, ); - } + // Update the max block number for the shared mutable read + if !private_call.public_inputs.call_context.contract_address.is_protocol_contract() { + self.public_inputs.validation_requests.max_block_number = MaxBlockNumber::min( + self.public_inputs.validation_requests.max_block_number, + MaxBlockNumber::new(compute_shared_mutable_block_horizon( + ScheduledValueChange::::unpack( + private_call.verification_key_hints.updated_class_id_value_change, + ), + ScheduledDelayChange::::unpack( + private_call.verification_key_hints.updated_class_id_delay_change, + ), + private_call.public_inputs.historical_header.global_variables.block_number + as u32, + )), + ); + } + } - fn propagate_note_hash_read_requests(&mut self, private_call: PrivateCircuitPublicInputs) { - let read_requests = private_call.note_hash_read_requests; + fn propagate_note_hash_read_requests( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { + let read_requests = private_call_public_inputs.note_hash_read_requests; for i in 0..read_requests.len() { let request = read_requests[i]; if !is_empty(request) { self.public_inputs.validation_requests.note_hash_read_requests.push(request.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } } - fn propagate_nullifier_read_requests(&mut self, private_call: PrivateCircuitPublicInputs) { - let nullifier_read_requests = private_call.nullifier_read_requests; + fn propagate_nullifier_read_requests( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { + let nullifier_read_requests = private_call_public_inputs.nullifier_read_requests; for i in 0..nullifier_read_requests.len() { let request = nullifier_read_requests[i]; if !is_empty(request) { self.public_inputs.validation_requests.nullifier_read_requests.push(request.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } } - fn propagate_key_validation_requests(&mut self, private_call: PrivateCircuitPublicInputs) { + fn propagate_key_validation_requests( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { let key_validation_requests_and_generators = - private_call.key_validation_requests_and_generators; + private_call_public_inputs.key_validation_requests_and_generators; for i in 0..key_validation_requests_and_generators.len() { let request = key_validation_requests_and_generators[i]; if !is_empty(request) { @@ -219,71 +249,79 @@ impl PrivateKernelCircuitPublicInputsComposer { .public_inputs .validation_requests .scoped_key_validation_requests_and_generators - .push(request.scope(private_call.call_context.contract_address)); + .push(request.scope(private_call_public_inputs.call_context.contract_address)); } } } - fn propagate_note_hashes(&mut self, private_call: PrivateCircuitPublicInputs) { - let note_hashes = private_call.note_hashes; + fn propagate_note_hashes(&mut self, private_call_public_inputs: PrivateCircuitPublicInputs) { + // BUG: If we delete this print, the resoluting note_hashes bounded vec is missing the original items. + no_op(self.public_inputs.end.note_hashes); + let note_hashes = private_call_public_inputs.note_hashes; for i in 0..note_hashes.len() { let note_hash = note_hashes[i]; if note_hash.value != 0 { self.public_inputs.end.note_hashes.push(note_hash.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } } - fn propagate_nullifiers(&mut self, private_call: PrivateCircuitPublicInputs) { - let nullifiers = private_call.nullifiers; + fn propagate_nullifiers(&mut self, private_call_public_inputs: PrivateCircuitPublicInputs) { + let nullifiers = private_call_public_inputs.nullifiers; for i in 0..nullifiers.len() { let nullifier = nullifiers[i]; if nullifier.value != 0 { self.public_inputs.end.nullifiers.push(nullifier.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } } - fn propagate_l2_to_l1_messages(&mut self, private_call: PrivateCircuitPublicInputs) { - let l2_to_l1_msgs = private_call.l2_to_l1_msgs; + fn propagate_l2_to_l1_messages( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { + let l2_to_l1_msgs = private_call_public_inputs.l2_to_l1_msgs; for i in 0..l2_to_l1_msgs.len() { let msg = l2_to_l1_msgs[i]; if !is_empty(msg) { self.public_inputs.end.l2_to_l1_msgs.push(msg.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } } - fn propagate_logs(&mut self, private_call: PrivateCircuitPublicInputs) { - let private_logs = private_call.private_logs; + fn propagate_logs(&mut self, private_call_public_inputs: PrivateCircuitPublicInputs) { + let private_logs = private_call_public_inputs.private_logs; for i in 0..private_logs.len() { let log = private_logs[i]; if !is_empty(log) { self.public_inputs.end.private_logs.push(log.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } - let contract_class_logs = private_call.contract_class_logs_hashes; + let contract_class_logs = private_call_public_inputs.contract_class_logs_hashes; for i in 0..contract_class_logs.len() { let log = contract_class_logs[i]; if !is_empty(log) { self.public_inputs.end.contract_class_logs_hashes.push(log.scope( - private_call.call_context.contract_address, + private_call_public_inputs.call_context.contract_address, )); } } } - fn propagate_private_call_requests(&mut self, private_call: PrivateCircuitPublicInputs) { - let call_requests = private_call.private_call_requests; + fn propagate_private_call_requests( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { + let call_requests = private_call_public_inputs.private_call_requests; let num_requests = array_length(call_requests); for i in 0..call_requests.len() { if i < num_requests { @@ -294,8 +332,11 @@ impl PrivateKernelCircuitPublicInputsComposer { } } - fn propagate_public_call_requests(&mut self, private_call: PrivateCircuitPublicInputs) { - let call_requests = private_call.public_call_requests; + fn propagate_public_call_requests( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { + let call_requests = private_call_public_inputs.public_call_requests; for i in 0..call_requests.len() { if !is_empty(call_requests[i]) { self.public_inputs.end.public_call_requests.push(call_requests[i]); @@ -303,8 +344,11 @@ impl PrivateKernelCircuitPublicInputsComposer { } } - fn propagate_public_teardown_call_request(&mut self, private_call: PrivateCircuitPublicInputs) { - let call_request = private_call.public_teardown_call_request; + fn propagate_public_teardown_call_request( + &mut self, + private_call_public_inputs: PrivateCircuitPublicInputs, + ) { + let call_request = private_call_public_inputs.public_teardown_call_request; if !is_empty(call_request) { assert( is_empty(self.public_inputs.public_teardown_call_request), @@ -314,10 +358,10 @@ impl PrivateKernelCircuitPublicInputsComposer { } } - fn propagate_fee_payer(&mut self, private_call: PrivateCircuitPublicInputs) { - if (private_call.is_fee_payer) { + fn propagate_fee_payer(&mut self, private_call_public_inputs: PrivateCircuitPublicInputs) { + if (private_call_public_inputs.is_fee_payer) { assert(self.public_inputs.fee_payer.is_zero(), "Cannot overwrite non-empty fee_payer"); - self.public_inputs.fee_payer = private_call.call_context.contract_address; + self.public_inputs.fee_payer = private_call_public_inputs.call_context.contract_address; } } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr index 99ba8a48532..849ede420af 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr @@ -43,16 +43,15 @@ impl PrivateKernelInitCircuitPrivateInputs { } unconstrained fn generate_output(self) -> PrivateKernelCircuitPublicInputs { - let private_call_public_inputs = self.private_call.public_inputs; PrivateKernelCircuitPublicInputsComposer::new_from_tx_request( self.tx_request, - private_call_public_inputs, + self.private_call.public_inputs, self.vk_tree_root, self.protocol_contract_tree_root, self.is_private_only, self.first_nullifier_hint, ) - .with_private_call(private_call_public_inputs) + .with_private_call(self.private_call) .finish() } @@ -77,7 +76,7 @@ impl PrivateKernelInitCircuitPrivateInputs { if dep::types::validate::should_validate_output() { PrivateKernelCircuitOutputValidator::new(output).validate_as_first_call( self.tx_request, - self.private_call.public_inputs, + self.private_call, private_call_data_validator.array_lengths, self.vk_tree_root, self.protocol_contract_tree_root, @@ -106,11 +105,14 @@ mod tests { pub fn new() -> Self { let private_call = FixtureBuilder::new().is_first_call(); let tx_request = private_call.build_tx_request(); + PrivateKernelInitInputsBuilder { tx_request, private_call } } - pub fn execute(self) -> PrivateKernelCircuitPublicInputs { + pub fn execute(mut self) -> PrivateKernelCircuitPublicInputs { + self.private_call.compute_update_tree_and_hints(); let private_call = self.private_call.to_private_call_data(); + PrivateKernelInitCircuitPrivateInputs { tx_request: self.tx_request, private_call, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index 0f1e4973666..24e1a784145 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -42,7 +42,7 @@ impl PrivateKernelInnerCircuitPrivateInputs { self.previous_kernel.public_inputs, ) .pop_top_call_request() - .with_private_call(self.private_call.public_inputs) + .with_private_call(self.private_call) .finish() } @@ -79,7 +79,7 @@ impl PrivateKernelInnerCircuitPrivateInputs { PrivateKernelCircuitOutputValidator::new(output).validate_as_inner_call( self.previous_kernel.public_inputs, previous_kernel_array_lengths, - self.private_call.public_inputs, + self.private_call, private_call_data_validator.array_lengths, ); } @@ -116,8 +116,10 @@ mod tests { } pub fn execute(&mut self) -> PrivateKernelCircuitPublicInputs { + self.private_call.compute_update_tree_and_hints(); let private_call = self.private_call.to_private_call_data(); self.previous_kernel.add_private_call_request_for_private_call(private_call); + self.previous_kernel.set_historical_header_from_call_data(private_call); let previous_kernel = self.previous_kernel.to_private_kernel_data(); let kernel = PrivateKernelInnerCircuitPrivateInputs { previous_kernel, private_call }; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/mod.nr index 606b05a0120..ea3d0aaaa80 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/mod.nr @@ -32,7 +32,9 @@ impl PrivateCallDataValidatorBuilder { } pub fn new_from_counter(counter: u32) -> Self { - let private_call = FixtureBuilder::new_from_counter(counter); + let mut private_call = FixtureBuilder::new_from_counter(counter); + private_call.compute_update_tree_and_hints(); + let previous_note_hashes = BoundedVec::new(); PrivateCallDataValidatorBuilder { private_call, previous_note_hashes } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_against_previous_kernel.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_against_previous_kernel.nr index 6878159ee2e..2462b5f5be2 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_against_previous_kernel.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_call_data_validator_builder/validate_against_previous_kernel.nr @@ -17,18 +17,19 @@ impl PrivateCallDataValidatorBuilder { previous_kernel, ); } -} -fn make_previous_kernel() -> PrivateKernelCircuitPublicInputs { - let builder = FixtureBuilder::new(); - builder.to_private_kernel_circuit_public_inputs() + fn make_previous_kernel(self) -> PrivateKernelCircuitPublicInputs { + let mut builder = FixtureBuilder::new(); + builder.historical_header = self.private_call.historical_header; + builder.to_private_kernel_circuit_public_inputs() + } } #[test] fn validate_against_previous_kernel_succeeds() { let builder = PrivateCallDataValidatorBuilder::new(); - let previous_kernel = make_previous_kernel(); + let previous_kernel = builder.make_previous_kernel(); builder.validate_against_previous_kernel(previous_kernel); } @@ -37,7 +38,7 @@ fn validate_against_previous_kernel_succeeds() { fn validate_against_previous_kernel_mismatch_header_version_fails() { let builder = PrivateCallDataValidatorBuilder::new(); - let mut previous_kernel = make_previous_kernel(); + let mut previous_kernel = builder.make_previous_kernel(); previous_kernel.constants.historical_header.global_variables.version += 1; builder.validate_against_previous_kernel(previous_kernel); @@ -47,7 +48,7 @@ fn validate_against_previous_kernel_mismatch_header_version_fails() { fn validate_against_previous_kernel_mismatch_chain_id_fails() { let builder = PrivateCallDataValidatorBuilder::new(); - let mut previous_kernel = make_previous_kernel(); + let mut previous_kernel = builder.make_previous_kernel(); previous_kernel.constants.tx_context.chain_id += 1; builder.validate_against_previous_kernel(previous_kernel); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/mod.nr index d392bdeeea1..8fffaf3fb31 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/mod.nr @@ -13,7 +13,7 @@ use dep::types::{ private_call_request::PrivateCallRequest, private_circuit_public_inputs::PrivateCircuitPublicInputsArrayLengths, }, - constants::PRIVATE_KERNEL_INIT_INDEX, + constants::{DEFAULT_UPDATE_DELAY, PRIVATE_KERNEL_INIT_INDEX}, tests::fixture_builder::FixtureBuilder, transaction::tx_request::TxRequest, }; @@ -33,6 +33,10 @@ impl PrivateKernelCircuitOutputValidatorBuilder { let mut output = FixtureBuilder::new(); let tx_request = output.build_tx_request(); output.claimed_first_nullifier = 27; + output.set_max_block_number( + (previous_kernel.historical_header.global_variables.block_number) as u32 + + DEFAULT_UPDATE_DELAY, + ); previous_kernel.claimed_first_nullifier = 27; previous_kernel = previous_kernel.in_vk_tree(PRIVATE_KERNEL_INIT_INDEX); @@ -74,7 +78,7 @@ impl PrivateKernelCircuitOutputValidatorBuilder { let output = self.output.to_private_kernel_circuit_public_inputs(); PrivateKernelCircuitOutputValidator::new(output).validate_as_first_call( self.tx_request, - private_call.public_inputs, + private_call, array_lengths, FixtureBuilder::vk_tree_root(), self.private_call.protocol_contract_tree_root, @@ -100,7 +104,7 @@ impl PrivateKernelCircuitOutputValidatorBuilder { PrivateKernelCircuitOutputValidator::new(output).validate_as_inner_call( previous_kernel, previous_kernel_array_lengths, - private_call.public_inputs, + private_call, private_call_array_lengths, ); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_aggregated_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_aggregated_values.nr index 9eb024dcb70..c1f7ce7772a 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_aggregated_values.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_aggregated_values.nr @@ -13,8 +13,8 @@ fn validate_aggregated_values_empty_data_succeeds() { fn validate_aggregated_values_min_revertible_side_effect_counter_from_previous_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.min_revertible_side_effect_counter = 123; - builder.output.min_revertible_side_effect_counter = 123; + builder.previous_kernel.min_revertible_side_effect_counter = 3; + builder.output.min_revertible_side_effect_counter = 3; builder.validate_as_inner_call(); } @@ -23,8 +23,8 @@ fn validate_aggregated_values_min_revertible_side_effect_counter_from_previous_s fn validate_aggregated_values_min_revertible_side_effect_counter_from_private_call_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.private_call.min_revertible_side_effect_counter = 123; - builder.output.min_revertible_side_effect_counter = 123; + builder.private_call.min_revertible_side_effect_counter = 3; + builder.output.min_revertible_side_effect_counter = 3; builder.validate_as_inner_call(); } @@ -33,9 +33,9 @@ fn validate_aggregated_values_min_revertible_side_effect_counter_from_private_ca fn validate_aggregated_values_min_revertible_side_effect_counter_overwrite_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.min_revertible_side_effect_counter = 123; + builder.previous_kernel.min_revertible_side_effect_counter = 3; builder.private_call.min_revertible_side_effect_counter = 4567; - builder.output.min_revertible_side_effect_counter = 123; + builder.output.min_revertible_side_effect_counter = 3; builder.validate_as_inner_call(); } @@ -44,7 +44,7 @@ fn validate_aggregated_values_min_revertible_side_effect_counter_overwrite_fails fn validate_aggregated_values_min_revertible_side_effect_counter_from_previous_mismatch_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.min_revertible_side_effect_counter = 123; + builder.previous_kernel.min_revertible_side_effect_counter = 3; builder.output.min_revertible_side_effect_counter = 4567; builder.validate_as_inner_call(); @@ -54,7 +54,7 @@ fn validate_aggregated_values_min_revertible_side_effect_counter_from_previous_m fn validate_aggregated_values_min_revertible_side_effect_counter_from_private_call_mismatch_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.private_call.min_revertible_side_effect_counter = 123; + builder.private_call.min_revertible_side_effect_counter = 3; builder.output.min_revertible_side_effect_counter = 4567; builder.validate_as_inner_call(); @@ -64,7 +64,7 @@ fn validate_aggregated_values_min_revertible_side_effect_counter_from_private_ca fn validate_aggregated_values_min_revertible_side_effect_counter_random_output_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.output.min_revertible_side_effect_counter = 123; + builder.output.min_revertible_side_effect_counter = 3; builder.validate_as_inner_call(); } @@ -76,8 +76,8 @@ fn validate_aggregated_values_min_revertible_side_effect_counter_random_output_f fn validate_aggregated_values_max_block_number_from_previous_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.set_max_block_number(123); - builder.output.set_max_block_number(123); + builder.previous_kernel.set_max_block_number(3); + builder.output.set_max_block_number(3); builder.validate_as_inner_call(); } @@ -86,8 +86,8 @@ fn validate_aggregated_values_max_block_number_from_previous_succeeds() { fn validate_aggregated_values_max_block_number_from_private_call_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.private_call.set_max_block_number(123); - builder.output.set_max_block_number(123); + builder.private_call.set_max_block_number(3); + builder.output.set_max_block_number(3); builder.validate_as_inner_call(); } @@ -96,9 +96,9 @@ fn validate_aggregated_values_max_block_number_from_private_call_succeeds() { fn validate_aggregated_values_max_block_number_from_both_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.set_max_block_number(123); - builder.private_call.set_max_block_number(123); - builder.output.set_max_block_number(123); + builder.previous_kernel.set_max_block_number(3); + builder.private_call.set_max_block_number(3); + builder.output.set_max_block_number(3); builder.validate_as_inner_call(); } @@ -107,9 +107,9 @@ fn validate_aggregated_values_max_block_number_from_both_succeeds() { fn validate_aggregated_values_max_block_number_from_both_pick_previous_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.set_max_block_number(123); - builder.private_call.set_max_block_number(4567); - builder.output.set_max_block_number(123); + builder.previous_kernel.set_max_block_number(3); + builder.private_call.set_max_block_number(4); + builder.output.set_max_block_number(3); builder.validate_as_inner_call(); } @@ -118,9 +118,9 @@ fn validate_aggregated_values_max_block_number_from_both_pick_previous_succeeds( fn validate_aggregated_values_max_block_number_from_both_pick_private_call_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.set_max_block_number(4567); - builder.private_call.set_max_block_number(123); - builder.output.set_max_block_number(123); + builder.previous_kernel.set_max_block_number(4); + builder.private_call.set_max_block_number(3); + builder.output.set_max_block_number(3); builder.validate_as_inner_call(); } @@ -129,9 +129,9 @@ fn validate_aggregated_values_max_block_number_from_both_pick_private_call_succe fn validate_aggregated_values_max_block_number_from_both_pick_larger_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.previous_kernel.set_max_block_number(4567); - builder.private_call.set_max_block_number(123); - builder.output.set_max_block_number(4567); + builder.previous_kernel.set_max_block_number(4); + builder.private_call.set_max_block_number(3); + builder.output.set_max_block_number(4); builder.validate_as_inner_call(); } @@ -140,7 +140,7 @@ fn validate_aggregated_values_max_block_number_from_both_pick_larger_fails() { fn validate_aggregated_values_max_block_number_random_output_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.output.set_max_block_number(123); + builder.output.set_max_block_number(3); builder.validate_as_inner_call(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_initial_values.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_initial_values.nr index 1a210916a54..acc97873be5 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_initial_values.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_initial_values.nr @@ -100,8 +100,8 @@ fn validate_initial_values_min_revertible_side_effect_counter_random_output_fail fn validate_initial_values_max_block_number_succeeds() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.private_call.set_max_block_number(123); - builder.output.set_max_block_number(123); + builder.private_call.set_max_block_number(3); + builder.output.set_max_block_number(3); builder.validate_as_first_call(false); } @@ -110,8 +110,8 @@ fn validate_initial_values_max_block_number_succeeds() { fn validate_initial_values_max_block_number_mismatch_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.private_call.set_max_block_number(4567); - builder.output.set_max_block_number(123); + builder.private_call.set_max_block_number(4); + builder.output.set_max_block_number(3); builder.validate_as_first_call(false); } @@ -120,7 +120,7 @@ fn validate_initial_values_max_block_number_mismatch_fails() { fn validate_initial_values_max_block_number_empty_output_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.private_call.set_max_block_number(4567); + builder.private_call.set_max_block_number(4); builder.validate_as_first_call(false); } @@ -129,7 +129,7 @@ fn validate_initial_values_max_block_number_empty_output_fails() { fn validate_initial_values_max_block_number_random_output_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.output.set_max_block_number(123); + builder.output.set_max_block_number(3); builder.validate_as_first_call(false); } @@ -215,7 +215,7 @@ fn validate_initial_values_fee_payer_empty_output_fails() { fn validate_initial_values_fee_payer_random_output_fails() { let mut builder = PrivateKernelCircuitOutputValidatorBuilder::new(); - builder.output.set_fee_payer(AztecAddress::from_field(123)); + builder.output.set_fee_payer(AztecAddress::from_field(3)); builder.validate_as_first_call(false); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/mod.nr index d699d987895..69ae6003782 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/mod.nr @@ -68,10 +68,7 @@ impl PrivateKernelCircuitPublicInputsComposerBuilder { ) -> PrivateKernelCircuitPublicInputs { let private_call = self.private_call.to_private_call_data(); unsafe { - self - .new_from_tx_request(is_private_only) - .with_private_call(private_call.public_inputs) - .finish() + self.new_from_tx_request(is_private_only).with_private_call(private_call).finish() } } @@ -88,7 +85,7 @@ impl PrivateKernelCircuitPublicInputsComposerBuilder { unsafe { PrivateKernelCircuitPublicInputsComposer::new_from_previous_kernel(previous_kernel) .pop_top_call_request() - .with_private_call(private_call.public_inputs) + .with_private_call(private_call) .finish() } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/new_from_previous_kernel_with_private_call.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/new_from_previous_kernel_with_private_call.nr index 8e4d03af9f4..b9b2f00a33c 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/new_from_previous_kernel_with_private_call.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/new_from_previous_kernel_with_private_call.nr @@ -1,7 +1,7 @@ use crate::tests::private_kernel_circuit_public_inputs_composer_builder::PrivateKernelCircuitPublicInputsComposerBuilder; use dep::types::{ abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputsArrayLengths, - tests::utils::assert_array_eq, traits::is_empty, + constants::DEFAULT_UPDATE_DELAY, tests::utils::assert_array_eq, traits::is_empty, }; #[test] @@ -14,7 +14,10 @@ fn new_from_previous_kernel_with_private_call_empty_data_succeeds() { let expected_array_lengths = PrivateKernelCircuitPublicInputsArrayLengths::empty(); assert_eq(array_lengths, expected_array_lengths); - assert(output.validation_requests.for_rollup.max_block_number.is_none()); + assert_eq( + output.validation_requests.for_rollup.max_block_number.unwrap_unchecked(), + DEFAULT_UPDATE_DELAY, + ); assert(is_empty(output.public_teardown_call_request)); assert(is_empty(output.fee_payer)); } @@ -23,30 +26,30 @@ fn new_from_previous_kernel_with_private_call_empty_data_succeeds() { fn new_from_previous_kernel_with_private_call_min_revertible_side_effect_counter_prev_empty_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.private_call.min_revertible_side_effect_counter = 123; + builder.private_call.min_revertible_side_effect_counter = 3; let output = builder.compose_from_previous_kernel(); - assert_eq(output.min_revertible_side_effect_counter, 123); + assert_eq(output.min_revertible_side_effect_counter, 3); } #[test] fn new_from_previous_kernel_with_private_call_min_revertible_side_effect_counter_curr_empty_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.previous_kernel.min_revertible_side_effect_counter = 123; + builder.previous_kernel.min_revertible_side_effect_counter = 3; let output = builder.compose_from_previous_kernel(); - assert_eq(output.min_revertible_side_effect_counter, 123); + assert_eq(output.min_revertible_side_effect_counter, 3); } #[test(should_fail_with = "cannot overwrite non-zero min_revertible_side_effect_counter")] fn new_from_previous_kernel_with_private_call_min_revertible_side_effect_counter_overwrite_fails() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.previous_kernel.min_revertible_side_effect_counter = 123; - builder.private_call.min_revertible_side_effect_counter = 123; + builder.previous_kernel.min_revertible_side_effect_counter = 3; + builder.private_call.min_revertible_side_effect_counter = 3; let _ = builder.compose_from_previous_kernel(); } @@ -55,46 +58,46 @@ fn new_from_previous_kernel_with_private_call_min_revertible_side_effect_counter fn new_from_previous_kernel_with_private_call_max_block_number_prev_empty_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.private_call.set_max_block_number(123); + builder.private_call.set_max_block_number(3); let output = builder.compose_from_previous_kernel(); - assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 123); + assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 3); } #[test] fn new_from_previous_kernel_with_private_call_max_block_number_curr_empty_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.previous_kernel.set_max_block_number(123); + builder.previous_kernel.set_max_block_number(3); let output = builder.compose_from_previous_kernel(); - assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 123); + assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 3); } #[test] fn new_from_previous_kernel_with_private_call_max_block_number_pick_prev_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.previous_kernel.set_max_block_number(123); - builder.private_call.set_max_block_number(4567); + builder.previous_kernel.set_max_block_number(3); + builder.private_call.set_max_block_number(4); let output = builder.compose_from_previous_kernel(); - assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 123); + assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 3); } #[test] fn new_from_previous_kernel_with_private_call_max_block_number_pick_curr_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.previous_kernel.set_max_block_number(4567); - builder.private_call.set_max_block_number(123); + builder.previous_kernel.set_max_block_number(4); + builder.private_call.set_max_block_number(3); let output = builder.compose_from_previous_kernel(); - assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 123); + assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 3); } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/propagate_from_private_call.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/propagate_from_private_call.nr index 66cfd54d85a..c1fc2dc9316 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/propagate_from_private_call.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_public_inputs_composer_builder/propagate_from_private_call.nr @@ -4,7 +4,7 @@ use crate::{ }; use dep::types::{ abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputsArrayLengths, - tests::utils::assert_array_eq, traits::is_empty, + constants::DEFAULT_UPDATE_DELAY, tests::utils::assert_array_eq, traits::is_empty, }; #[test] @@ -25,8 +25,18 @@ fn propagate_from_private_call_empty_data_succeeds() { assert_eq(array_lengths, expected_array_lengths); assert_eq(output.min_revertible_side_effect_counter, 0); - assert(is_empty(output.validation_requests)); - assert(output.validation_requests.for_rollup.max_block_number.is_none()); + assert(is_empty(output.validation_requests.note_hash_read_requests)); + assert(is_empty(output.validation_requests.nullifier_read_requests)); + assert( + is_empty( + output.validation_requests.scoped_key_validation_requests_and_generators, + ), + ); + assert(is_empty(output.validation_requests.split_counter)); + assert_eq( + output.validation_requests.for_rollup.max_block_number.unwrap_unchecked(), + DEFAULT_UPDATE_DELAY, + ); assert(is_empty(output.public_teardown_call_request)); assert(is_empty(output.fee_payer)); } @@ -46,11 +56,11 @@ fn propagate_from_private_call_min_revertible_side_effect_counter_succeeds() { fn propagate_from_private_call_max_block_number_succeeds() { let mut builder = PrivateKernelCircuitPublicInputsComposerBuilder::new(); - builder.private_call.set_max_block_number(123); + builder.private_call.set_max_block_number(5); let output = builder.compose_from_tx_request(false); - assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 123); + assert_eq(output.validation_requests.for_rollup.max_block_number.unwrap(), 5); } #[test] 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 37c1b1dca94..c3f76353eb6 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 @@ -3,7 +3,9 @@ use crate::{ address::SaltedInitializationHash, constants::{ FUNCTION_TREE_HEIGHT, PROOF_TYPE_OINK, PROOF_TYPE_PG, PROTOCOL_CONTRACT_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, }, + data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, merkle_tree::membership::MembershipWitness, proof::verification_key::ClientIVCVerificationKey, public_keys::PublicKeys, @@ -11,17 +13,8 @@ use crate::{ pub struct PrivateCallData { pub public_inputs: PrivateCircuitPublicInputs, - pub vk: ClientIVCVerificationKey, - - pub salted_initialization_hash: SaltedInitializationHash, - pub public_keys: PublicKeys, - pub contract_class_artifact_hash: Field, - pub contract_class_public_bytecode_commitment: Field, - pub function_leaf_membership_witness: MembershipWitness, - pub protocol_contract_sibling_path: [Field; PROTOCOL_CONTRACT_TREE_HEIGHT], - - pub acir_hash: Field, + pub verification_key_hints: PrivateVerificationKeyHints, } impl PrivateCallData { @@ -36,17 +29,23 @@ impl PrivateCallData { } } -pub struct PrivateCallDataWithoutPublicInputs { - pub vk: ClientIVCVerificationKey, - +pub struct PrivateVerificationKeyHints { pub salted_initialization_hash: SaltedInitializationHash, pub public_keys: PublicKeys, pub contract_class_artifact_hash: Field, pub contract_class_public_bytecode_commitment: Field, pub function_leaf_membership_witness: MembershipWitness, pub protocol_contract_sibling_path: [Field; PROTOCOL_CONTRACT_TREE_HEIGHT], - pub acir_hash: Field, + pub updated_class_id_witness: MembershipWitness, + pub updated_class_id_leaf: PublicDataTreeLeafPreimage, + pub updated_class_id_value_change: [Field; 3], + pub updated_class_id_delay_change: [Field; 1], +} + +pub struct PrivateCallDataWithoutPublicInputs { + pub vk: ClientIVCVerificationKey, + pub verification_key_hints: PrivateVerificationKeyHints, } impl PrivateCallDataWithoutPublicInputs { @@ -57,14 +56,7 @@ impl PrivateCallDataWithoutPublicInputs { PrivateCallData { public_inputs, vk: self.vk, - salted_initialization_hash: self.salted_initialization_hash, - public_keys: self.public_keys, - contract_class_artifact_hash: self.contract_class_artifact_hash, - contract_class_public_bytecode_commitment: self - .contract_class_public_bytecode_commitment, - function_leaf_membership_witness: self.function_leaf_membership_witness, - protocol_contract_sibling_path: self.protocol_contract_sibling_path, - acir_hash: self.acir_hash, + verification_key_hints: self.verification_key_hints, } } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index 2d7250d531c..eeb33844923 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -1,15 +1,13 @@ use crate::{ - abis::function_selector::FunctionSelector, address::{ partial_address::PartialAddress, salted_initialization_hash::SaltedInitializationHash, }, constants::{ - AZTEC_ADDRESS_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__CONTRACT_ADDRESS_V1, - MAX_FIELD_VALUE, + AZTEC_ADDRESS_LENGTH, GENERATOR_INDEX__CONTRACT_ADDRESS_V1, MAX_FIELD_VALUE, + MAX_PROTOCOL_CONTRACTS, }, contract_class_id::ContractClassId, - hash::{poseidon2_hash_with_separator, private_functions_root_from_siblings}, - merkle_tree::membership::MembershipWitness, + hash::poseidon2_hash_with_separator, public_keys::{IvpkM, NpkM, OvpkM, PublicKeys, ToPoint, TpkM}, traits::{Deserialize, Empty, FromField, Packable, Serialize, ToField}, }; @@ -124,29 +122,11 @@ impl AztecAddress { AztecAddress::from_field(address_point.x) } - pub fn compute_from_private_function( - function_selector: FunctionSelector, - function_vk_hash: Field, - function_leaf_membership_witness: MembershipWitness, - contract_class_artifact_hash: Field, - contract_class_public_bytecode_commitment: Field, + pub fn compute_from_class_id( + contract_class_id: ContractClassId, salted_initialization_hash: SaltedInitializationHash, public_keys: PublicKeys, ) -> Self { - let private_functions_root = private_functions_root_from_siblings( - function_selector, - function_vk_hash, - function_leaf_membership_witness.leaf_index, - function_leaf_membership_witness.sibling_path, - ); - - let contract_class_id = ContractClassId::compute( - contract_class_artifact_hash, - private_functions_root, - contract_class_public_bytecode_commitment, - ); - - // Compute contract address using the preimage which includes the class_id. let partial_address = PartialAddress::compute_from_salted_initialization_hash( contract_class_id, salted_initialization_hash, @@ -155,6 +135,10 @@ impl AztecAddress { AztecAddress::compute(public_keys, partial_address) } + pub fn is_protocol_contract(self) -> bool { + self.inner.lt(MAX_PROTOCOL_CONTRACTS as Field) + } + pub fn is_zero(self) -> bool { self.inner == 0 } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 2b2027457a6..ea65b288900 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -171,6 +171,10 @@ pub global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE: Field = pub global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE: Field = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; +// sha224sum 'struct ContractInstanceUpdated' +pub global DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE: Field = + 0x0e92f9f8a534e858fcf777da206e08b0c620ecf9deb21d13479813f6; + // CANONICAL CONTRACT ADDRESSES pub global MAX_PROTOCOL_CONTRACTS: u32 = (1 << PROTOCOL_CONTRACT_TREE_HEIGHT as u8) - 1; // Index 0 can't be used. pub global CANONICAL_AUTH_REGISTRY_ADDRESS: AztecAddress = AztecAddress::from_field(1); @@ -183,6 +187,12 @@ pub global ROUTER_ADDRESS: AztecAddress = AztecAddress::from_field(6); // Slot of the balances map to be hashed with an AztecAddress (map key) to get an actual storage slot. pub global FEE_JUICE_BALANCES_SLOT: u32 = 1; +// Slot of the updated_class_ids map to be hashed with an AztecAddress (map key) to get an actual storage slot. +pub global UPDATED_CLASS_IDS_SLOT: u32 = 1; +pub global SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR: u32 = 0; +pub global SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR: u32 = 1; +pub global SHARED_MUTABLE_HASH_SEPARATOR: u32 = 2; + // CANONICAL DEFAULT KEYS // This below are: // "az_null_npk" @@ -761,6 +771,8 @@ pub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 6; pub global TWO_POW_64: Field = 18446744073709551616; +pub global DEFAULT_UPDATE_DELAY: u32 = 10; + mod test { use crate::constants::{ MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr index 2ff06c1d8e4..fb68c541257 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contract_class_id.nr @@ -1,5 +1,5 @@ use crate::constants::GENERATOR_INDEX__CONTRACT_LEAF; -use crate::traits::{Deserialize, FromField, Packable, Serialize, ToField}; +use crate::traits::{Deserialize, Empty, FromField, Packable, Serialize, ToField}; pub struct ContractClassId { pub inner: Field, @@ -35,6 +35,12 @@ impl Deserialize<1> for ContractClassId { } } +impl Empty for ContractClassId { + fn empty() -> Self { + Self { inner: 0 } + } +} + // Implement the Packable trait so ContractClassId can be stored in contract's storage. impl Packable<1> for ContractClassId { fn pack(self) -> [Field; 1] { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/debug.nr b/noir-projects/noir-protocol-circuits/crates/types/src/debug.nr new file mode 100644 index 00000000000..31ccafc068f --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/debug.nr @@ -0,0 +1,16 @@ +// Temporary workaround to bypass compiler issues. Passing values to the oracle is able to fix some bugs such as https://github.com/noir-lang/noir/issues/7192 +// and https://github.com/noir-lang/noir/issues/7192 + +#[oracle(noOp)] +fn no_op_oracle(value: T) {} + +unconstrained fn no_op_oracle_wrapper(value: T) { + no_op_oracle(value); +} + +pub fn no_op(value: T) { + /// Safety: This is a no op + unsafe { + no_op_oracle_wrapper(value); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr b/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr index c5c37d539cb..754969573fe 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/lib.nr @@ -1,6 +1,7 @@ pub mod utils; pub mod address; pub mod debug_log; +pub mod debug; pub mod public_keys; pub mod point; pub mod scalar; @@ -18,6 +19,7 @@ pub mod traits; pub mod type_serialization; pub mod type_packing; +pub mod shared_mutable; pub mod content_commitment; pub mod block_header; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/mod.nr new file mode 100644 index 00000000000..653b975e0da --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/mod.nr @@ -0,0 +1,121 @@ +use super::{ + address::aztec_address::AztecAddress, + block_header::BlockHeader, + constants::{ + GENERATOR_INDEX__PUBLIC_LEAF_INDEX, PUBLIC_DATA_TREE_HEIGHT, SHARED_MUTABLE_HASH_SEPARATOR, + }, + data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, + hash::{poseidon2_hash, poseidon2_hash_with_separator}, + merkle_tree::{membership::MembershipWitness, root::root_from_sibling_path}, + traits::{Empty, FromField, Hash, Packable, ToField}, + utils::arrays::array_concat, +}; +use scheduled_delay_change::ScheduledDelayChange; +use scheduled_value_change::ScheduledValueChange; + +pub mod scheduled_delay_change; +pub mod scheduled_value_change; + +pub fn validate_shared_mutable_hints( + historical_header: BlockHeader, + shared_mutable_storage_slot: Field, + contract_address: AztecAddress, + value_change_hint: ScheduledValueChange, + delay_change_hint: ScheduledDelayChange, + witness: MembershipWitness, + leaf_preimage: PublicDataTreeLeafPreimage, +) +where + T: ToField + Eq + FromField + Empty, +{ + let hash = public_storage_historical_read( + historical_header, + poseidon2_hash_with_separator([shared_mutable_storage_slot], SHARED_MUTABLE_HASH_SEPARATOR), + contract_address, + witness, + leaf_preimage, + ); + + if hash != 0 { + assert_eq( + hash, + hash_scheduled_data(value_change_hint, delay_change_hint), + "Hint values do not match hash", + ); + } else { + assert_eq( + value_change_hint, + ScheduledValueChange::empty(), + "Non-zero value change for zero hash", + ); + assert_eq( + delay_change_hint, + ScheduledDelayChange::empty(), + "Non-zero delay change for zero hash", + ); + }; +} + +pub fn compute_shared_mutable_block_horizon( + value_change: ScheduledValueChange, + delay_change: ScheduledDelayChange, + historical_block_number: u32, +) -> u32 +where + T: ToField + Eq + FromField, +{ + let effective_minimum_delay = + delay_change.get_effective_minimum_delay_at(historical_block_number); + value_change.get_block_horizon(historical_block_number, effective_minimum_delay) +} + +fn public_storage_historical_read( + historical_header: BlockHeader, + storage_slot: Field, + contract_address: AztecAddress, + witness: MembershipWitness, + leaf_preimage: PublicDataTreeLeafPreimage, +) -> Field { + let public_data_tree_index = poseidon2_hash_with_separator( + [contract_address.to_field(), storage_slot], + GENERATOR_INDEX__PUBLIC_LEAF_INDEX, + ); + + assert_eq( + historical_header.state.partial.public_data_tree.root, + root_from_sibling_path( + leaf_preimage.hash(), + witness.leaf_index, + witness.sibling_path, + ), + "Proving public value inclusion failed", + ); + + let is_less_than_slot = leaf_preimage.slot.lt(public_data_tree_index); + let is_next_greater_than = public_data_tree_index.lt(leaf_preimage.next_slot); + let is_max = ((leaf_preimage.next_index == 0) & (leaf_preimage.next_slot == 0)); + let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); + + if is_in_range { + 0 + } else { + assert_eq( + leaf_preimage.slot, + public_data_tree_index, + "Public data tree index doesn't match witness", + ); + leaf_preimage.value + } +} + +fn hash_scheduled_data( + value_change: ScheduledValueChange, + delay_change: ScheduledDelayChange, +) -> Field +where + T: ToField + Eq + FromField, +{ + let concatenated: [Field; 4] = array_concat(value_change.pack(), delay_change.pack()); + poseidon2_hash(concatenated) +} + diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_delay_change.nr b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_delay_change.nr similarity index 92% rename from noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_delay_change.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_delay_change.nr index d325dbd0a0f..db4e3a47dba 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_delay_change.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_delay_change.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::traits::Packable; +use crate::traits::{Empty, Packable}; use std::cmp::min; mod test; @@ -9,7 +9,7 @@ mod test; // is performed via `schedule_change` in order to satisfy ScheduleValueChange constraints: if e.g. we allowed for the // delay to be decreased immediately then it'd be possible for the state variable to schedule a value change with a // reduced delay, invalidating prior private reads. -pub(crate) struct ScheduledDelayChange { +pub struct ScheduledDelayChange { // Both pre and post are stored in public storage, so by default they are zeroed. By wrapping them in an Option, // they default to Option::none(), which we detect and replace with INITIAL_DELAY. The end result is that a // ScheduledDelayChange that has not been initialized has a delay equal to INITIAL_DELAY, which is the desired @@ -21,14 +21,14 @@ pub(crate) struct ScheduledDelayChange { } impl ScheduledDelayChange { - pub(crate) fn new(pre: Option, post: Option, block_of_change: u32) -> Self { + pub fn new(pre: Option, post: Option, block_of_change: u32) -> Self { Self { pre, post, block_of_change } } /// Returns the current value of the delay stored in the data structure. /// This function only returns a meaningful value when called in public with the current block number - for /// historical private reads use `get_effective_minimum_delay_at` instead. - pub(crate) fn get_current(self, current_block_number: u32) -> u32 { + pub fn get_current(self, current_block_number: u32) -> u32 { // The post value becomes the current one at the block of change, so any transaction that is included in the // block of change will use the post value. if current_block_number < self.block_of_change { @@ -41,7 +41,7 @@ impl ScheduledDelayChange { /// Returns the scheduled change, i.e. the post-change delay and the block at which it will become the current /// delay. Note that this block may be in the past if the change has already taken place. /// Additionally, further changes might be later scheduled, potentially canceling the one returned by this function. - pub(crate) fn get_scheduled(self) -> (u32, u32) { + pub fn get_scheduled(self) -> (u32, u32) { (self.post.unwrap_or(INITIAL_DELAY), self.block_of_change) } @@ -52,7 +52,7 @@ impl ScheduledDelayChange { /// - when reducing the delay, the change will take effect after a delay equal to the difference between old and /// new delay. For example, if reducing from 3 days to 1 day, the reduction will be scheduled to happen after 2 /// days. - pub(crate) fn schedule_change(&mut self, new: u32, current_block_number: u32) { + pub fn schedule_change(&mut self, new: u32, current_block_number: u32) { let current = self.get_current(current_block_number); // When changing the delay value we must ensure that it is not possible to produce a value change with a delay @@ -89,7 +89,7 @@ impl ScheduledDelayChange { /// which a value change could be scheduled), but it also considers scenarios in which a delay reduction is /// scheduled to happen in the near future, resulting in a way to schedule a change with an overall delay lower than /// the current one. - pub(crate) fn get_effective_minimum_delay_at(self, historical_block_number: u32) -> u32 { + pub fn get_effective_minimum_delay_at(self, historical_block_number: u32) -> u32 { if self.block_of_change <= historical_block_number { // If no delay changes were scheduled, then the delay value at the historical block (post) is guaranteed to // hold due to how further delay changes would be scheduled by `schedule_change`. @@ -129,7 +129,7 @@ impl Packable<1> for ScheduledDelayChange fn pack(self) -> [Field; 1] { // We pack all three u32 values into a single U128, which is made up of two u64 limbs. // Low limb: [ pre_inner: u32 | post_inner: u32 ] - // High limb: [ empty | pre_is_some: u8 | post_is_some: u8 | block_of_change: u32 ] + // High limb: [ empty | pre_is_some: u1 | post_is_some: u1 | block_of_change: u32 ] let lo = ((self.pre.unwrap_unchecked() as u64) * (1 << 32)) + (self.post.unwrap_unchecked() as u64); @@ -177,3 +177,9 @@ impl Eq for ScheduledDelayChange { & (self.block_of_change == other.block_of_change) } } + +impl Empty for ScheduledDelayChange { + fn empty() -> Self { + Self { pre: Option::none(), post: Option::none(), block_of_change: 0 } + } +} diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_delay_change/test.nr b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_delay_change/test.nr similarity index 99% rename from noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_delay_change/test.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_delay_change/test.nr index ff83d72da6c..fc7778b0256 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_delay_change/test.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_delay_change/test.nr @@ -1,4 +1,4 @@ -use crate::state_vars::shared_mutable::scheduled_delay_change::ScheduledDelayChange; +use crate::shared_mutable::scheduled_delay_change::ScheduledDelayChange; global TEST_INITIAL_DELAY: u32 = 13; diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_value_change.nr b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_value_change.nr similarity index 90% rename from noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_value_change.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_value_change.nr index eeb55c46e76..4acf8159646 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_value_change.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_value_change.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::traits::{FromField, Packable, ToField}; +use crate::traits::{Empty, FromField, Packable, ToField}; use std::cmp::min; mod test; @@ -8,7 +8,7 @@ mod test; // of change after some minimum delay measured in blocks has elapsed. This means that at any given block number we know // both the current value and the smallest block number at which the value might change - this is called the // 'block horizon'. -pub(crate) struct ScheduledValueChange { +pub struct ScheduledValueChange { pre: T, post: T, // Block at which `post` value is used instead of `pre` @@ -16,7 +16,7 @@ pub(crate) struct ScheduledValueChange { } impl ScheduledValueChange { - pub(crate) fn new(pre: T, post: T, block_of_change: u32) -> Self { + pub fn new(pre: T, post: T, block_of_change: u32) -> Self { Self { pre, post, block_of_change } } @@ -26,7 +26,7 @@ impl ScheduledValueChange { /// to construct the proof). /// Reading in private is only safe if the transaction's `max_block_number` property is set to a value lower or /// equal to the block horizon (see `get_block_horizon()`). - pub(crate) fn get_current_at(self, block_number: u32) -> T { + pub fn get_current_at(self, block_number: u32) -> T { // The post value becomes the current one at the block of change. This means different things in each realm: // - in public, any transaction that is included in the block of change will use the post value // - in private, any transaction that includes the block of change as part of the historical state will use the @@ -41,10 +41,16 @@ impl ScheduledValueChange { /// Returns the scheduled change, i.e. the post-change value and the block at which it will become the current /// value. Note that this block may be in the past if the change has already taken place. /// Additionally, further changes might be later scheduled, potentially canceling the one returned by this function. - pub(crate) fn get_scheduled(self) -> (T, u32) { + pub fn get_scheduled(self) -> (T, u32) { (self.post, self.block_of_change) } + // Returns the previous value. This is the value that is current up until the block of change. Note that this value + // might not be the current anymore since block of change might have already passed. + pub fn get_previous(self) -> (T, u32) { + (self.pre, self.block_of_change) + } + /// Returns the largest block number at which the value returned by `get_current_at` is known to remain the current /// value. This value is only meaningful in private when constructing a proof at some `historical_block_number`, /// since due to its asynchronous nature private execution cannot know about any later scheduled changes. @@ -56,7 +62,7 @@ impl ScheduledValueChange { /// The value returned by `get_current_at` in private when called with a historical block number is only safe to use /// if the transaction's `max_block_number` property is set to a value lower or equal to the block horizon computed /// using the same historical block number. - pub(crate) fn get_block_horizon(self, historical_block_number: u32, minimum_delay: u32) -> u32 { + pub fn get_block_horizon(self, historical_block_number: u32, minimum_delay: u32) -> u32 { // The block horizon is the very last block in which the current value is known. Any block past the horizon // (i.e. with a block number larger than the block horizon) may have a different current value. Reading the // current value in private typically requires constraining the maximum valid block number to be equal to the @@ -118,7 +124,7 @@ impl ScheduledValueChange { /// Mutates the value by scheduling a change at the current block number. This function is only meaningful when /// called in public with the current block number. - pub(crate) fn schedule_change( + pub fn schedule_change( &mut self, new_value: T, current_block_number: u32, @@ -160,3 +166,12 @@ where & (self.block_of_change == other.block_of_change) } } + +impl Empty for ScheduledValueChange +where + T: Empty, +{ + fn empty() -> Self { + Self { pre: T::empty(), post: T::empty(), block_of_change: 0 } + } +} diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_value_change/test.nr b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_value_change/test.nr similarity index 98% rename from noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_value_change/test.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_value_change/test.nr index 179089945ab..4b66df4955d 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/scheduled_value_change/test.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/shared_mutable/scheduled_value_change/test.nr @@ -1,4 +1,4 @@ -use crate::state_vars::shared_mutable::scheduled_value_change::ScheduledValueChange; +use crate::shared_mutable::scheduled_value_change::ScheduledValueChange; global TEST_DELAY: u32 = 200; 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 a043820e186..24c69932008 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 @@ -25,7 +25,7 @@ use crate::{ nullifier::{Nullifier, ScopedNullifier}, private_call_request::PrivateCallRequest, private_circuit_public_inputs::PrivateCircuitPublicInputs, - private_kernel::private_call_data::PrivateCallData, + private_kernel::private_call_data::{PrivateCallData, PrivateVerificationKeyHints}, private_kernel_data::PrivateKernelData, private_log::PrivateLogData, public_call_request::PublicCallRequest, @@ -43,7 +43,8 @@ use crate::{ address::{AztecAddress, EthAddress, SaltedInitializationHash}, block_header::BlockHeader, constants::{ - CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, FUNCTION_TREE_HEIGHT, + CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS, DEFAULT_UPDATE_DELAY, + DEPLOYER_CONTRACT_ADDRESS, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__PUBLIC_LEAF_INDEX, MAX_CONTRACT_CLASS_LOGS_PER_TX, MAX_ENQUEUED_CALLS_PER_TX, MAX_FIELD_VALUE, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NOTE_HASHES_PER_TX, @@ -51,11 +52,14 @@ use crate::{ MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PRIVATE_LOGS_PER_TX, MAX_PUBLIC_LOGS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PRIVATE_CALL_REQUEST_LENGTH, PRIVATE_LOG_SIZE_IN_FIELDS, PROTOCOL_CONTRACT_TREE_HEIGHT, PUBLIC_CALL_REQUEST_LENGTH, - PUBLIC_LOG_DATA_SIZE_IN_FIELDS, VK_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, PUBLIC_LOG_DATA_SIZE_IN_FIELDS, SHARED_MUTABLE_HASH_SEPARATOR, + UPDATED_CLASS_IDS_SLOT, VK_TREE_HEIGHT, }, + contract_class_id::ContractClassId, + data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, hash::{ compute_l2_to_l1_hash, compute_siloed_nullifier, compute_siloed_private_log_field, - compute_unique_siloed_note_hash, silo_note_hash, + compute_unique_siloed_note_hash, poseidon2_hash_with_separator, silo_note_hash, }, merkle_tree::{membership::MembershipWitness, MerkleTree}, messaging::l2_to_l1_message::{L2ToL1Message, ScopedL2ToL1Message}, @@ -66,8 +70,16 @@ use crate::{ verification_key::{ClientIVCVerificationKey, HonkVerificationKey, VerificationKey}, }, public_keys::PublicKeys, - tests::fixtures::{self, contract_functions::ContractFunction, contracts::ContractData}, - traits::{Deserialize, Empty, FromField}, + shared_mutable::{ + hash_scheduled_data, scheduled_delay_change::ScheduledDelayChange, + scheduled_value_change::ScheduledValueChange, + }, + storage::map::derive_storage_slot_in_map, + tests::fixtures::{ + self, contract_functions::ContractFunction, contracts::ContractData, + public_data_tree::empty_public_data_tree, + }, + traits::{Deserialize, Empty, FromField, Hash, is_empty, Packable, ToField}, transaction::{tx_context::TxContext, tx_request::TxRequest}, }; @@ -161,6 +173,12 @@ pub struct FixtureBuilder { pub protocol_contract_tree_root: Field, pub protocol_contract_sibling_path: [Field; PROTOCOL_CONTRACT_TREE_HEIGHT], + // Contract updates + pub updated_class_id_witness: MembershipWitness, + pub updated_class_id_leaf: PublicDataTreeLeafPreimage, + pub updated_class_id_value_change: ScheduledValueChange, + pub updated_class_id_delay_change: ScheduledDelayChange, + // Tree snapshots. pub archive_tree: AppendOnlyTreeSnapshot, @@ -380,10 +398,8 @@ impl FixtureBuilder { } } - pub fn to_private_call_data(self) -> PrivateCallData { - PrivateCallData { - public_inputs: self.to_private_circuit_public_inputs(), - vk: self.client_ivc_vk, + pub fn to_private_verification_key_hints(self) -> PrivateVerificationKeyHints { + PrivateVerificationKeyHints { function_leaf_membership_witness: self.function_leaf_membership_witness, salted_initialization_hash: self.salted_initialization_hash, public_keys: self.public_keys, @@ -392,6 +408,84 @@ impl FixtureBuilder { .contract_class_public_bytecode_commitment, protocol_contract_sibling_path: self.protocol_contract_sibling_path, acir_hash: self.acir_hash, + updated_class_id_witness: self.updated_class_id_witness, + updated_class_id_leaf: self.updated_class_id_leaf, + updated_class_id_value_change: self.updated_class_id_value_change.pack(), + updated_class_id_delay_change: self.updated_class_id_delay_change.pack(), + } + } + + pub fn compute_update_tree_and_hints(&mut self) { + let public_data_prefill = 2; + let mut public_data_tree = empty_public_data_tree::<8, 3>(public_data_prefill); + + if is_empty(self.updated_class_id_value_change) + & is_empty(self.updated_class_id_delay_change) { + self.historical_header.state.partial.public_data_tree.root = + public_data_tree.get_root(); + self.historical_header.state.partial.public_data_tree.next_available_leaf_index = + public_data_prefill; + + self.updated_class_id_witness = MembershipWitness { + leaf_index: (public_data_prefill - 1) as Field, + sibling_path: public_data_tree.get_sibling_path(public_data_prefill - 1), + }; + self.updated_class_id_leaf = PublicDataTreeLeafPreimage { + slot: (public_data_prefill - 1) as Field, + value: 0, + next_slot: 0, + next_index: 0, + }; + } else { + let hashed_update = hash_scheduled_data( + self.updated_class_id_value_change, + self.updated_class_id_delay_change, + ); + let shared_mutable_slot = + derive_storage_slot_in_map(UPDATED_CLASS_IDS_SLOT as Field, self.contract_address); + let hash_slot = + poseidon2_hash_with_separator([shared_mutable_slot], SHARED_MUTABLE_HASH_SEPARATOR); + let hash_leaf_slot = poseidon2_hash_with_separator( + [DEPLOYER_CONTRACT_ADDRESS.to_field(), hash_slot], + GENERATOR_INDEX__PUBLIC_LEAF_INDEX, + ); + public_data_tree.update_leaf( + public_data_prefill - 1, + PublicDataTreeLeafPreimage { + slot: (public_data_prefill - 1) as Field, + value: 0, + next_slot: hash_leaf_slot, + next_index: public_data_prefill, + } + .hash(), + ); + self.updated_class_id_leaf = PublicDataTreeLeafPreimage { + slot: hash_leaf_slot, + value: hashed_update, + next_slot: 0, + next_index: 0, + }; + public_data_tree.update_leaf(public_data_prefill, self.updated_class_id_leaf.hash()); + self.historical_header.state.partial.public_data_tree.root = + public_data_tree.get_root(); + self.historical_header.state.partial.public_data_tree.next_available_leaf_index = + public_data_prefill + 1; + self.updated_class_id_witness = MembershipWitness { + leaf_index: public_data_prefill as Field, + sibling_path: public_data_tree.get_sibling_path(public_data_prefill), + }; + } + } + + pub fn set_historical_header_from_call_data(&mut self, private_call: PrivateCallData) { + self.historical_header = private_call.public_inputs.historical_header; + } + + pub fn to_private_call_data(mut self) -> PrivateCallData { + PrivateCallData { + public_inputs: self.to_private_circuit_public_inputs(), + vk: self.client_ivc_vk, + verification_key_hints: self.to_private_verification_key_hints(), } } @@ -1137,6 +1231,10 @@ 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], + updated_class_id_witness: MembershipWitness::empty(), + updated_class_id_leaf: PublicDataTreeLeafPreimage::empty(), + updated_class_id_value_change: ScheduledValueChange::empty(), + updated_class_id_delay_change: ScheduledDelayChange::empty(), archive_tree: AppendOnlyTreeSnapshot::zero(), revert_code: 0, min_revertible_side_effect_counter: 0, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures.nr index 4c9e41cadb7..f95a5e0e2b2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures.nr @@ -3,6 +3,7 @@ pub mod contracts; pub mod merkle_tree; pub mod protocol_contract_tree; pub mod vk_tree; +pub mod public_data_tree; use crate::address::AztecAddress; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/public_data_tree.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/public_data_tree.nr new file mode 100644 index 00000000000..36188c5a825 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/public_data_tree.nr @@ -0,0 +1,35 @@ +use crate::{ + constants::PUBLIC_DATA_TREE_HEIGHT, + data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, + tests::merkle_tree_utils::NonEmptyMerkleTree, traits::Hash, +}; + +pub fn empty_public_data_tree( + prefill_count: u32, + ) -> NonEmptyMerkleTree { + let mut leaves = [0; LEAVES_COUNT]; + for i in 0..(prefill_count - 1) { + let leaf = PublicDataTreeLeafPreimage { + slot: i as Field, + value: 0, + next_slot: (i + 1) as Field, + next_index: i + 1, + }; + leaves[i] = leaf.hash(); + } + + let last_leaf = PublicDataTreeLeafPreimage { + slot: (prefill_count - 1) as Field, + value: 0, + next_slot: 0, + next_index: 0, + }; + leaves[prefill_count - 1] = last_leaf.hash(); + + NonEmptyMerkleTree::new( + leaves, + [0; PUBLIC_DATA_TREE_HEIGHT], + [0; PUBLIC_DATA_TREE_HEIGHT - SUBTREE_HEIGHT], + [0; SUBTREE_HEIGHT], + ) +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr index 52290e27603..97ae46c48c4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr @@ -189,6 +189,7 @@ impl u32 { SUBTREE_ITEMS } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr index 2ecb40dd98a..2a0ae428a92 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr @@ -48,6 +48,21 @@ impl Empty for U128 { } } +impl Empty for [T; N] +where + T: Empty, +{ + fn empty() -> Self { + [T::empty(); N] + } +} + +impl Empty for Option { + fn empty() -> Self { + Option::none() + } +} + pub fn is_empty(item: T) -> bool where T: Empty + Eq, diff --git a/yarn-project/accounts/src/testing/create_account.ts b/yarn-project/accounts/src/testing/create_account.ts index 617f950a3e5..31aa387649f 100644 --- a/yarn-project/accounts/src/testing/create_account.ts +++ b/yarn-project/accounts/src/testing/create_account.ts @@ -48,7 +48,8 @@ export async function createAccounts( if (index === 0) { // for the first account, check if the contract class is already registered, otherwise we should register now if ( - !(await pxe.getContractClassMetadata(account.getInstance().contractClassId)).isContractClassPubliclyRegistered + !(await pxe.getContractClassMetadata(account.getInstance().currentContractClassId)) + .isContractClassPubliclyRegistered ) { skipClassRegistration = false; } diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 3467d2c6df3..c78c257a3d5 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -32,6 +32,7 @@ import { type FunctionSelector, type PrivateLog, type PublicFunction, + type PublicLog, type UnconstrainedFunctionWithMembershipProof, computePublicBytecodeCommitment, isValidPrivateFunctionMembershipProof, @@ -51,7 +52,10 @@ import { PrivateFunctionBroadcastedEvent, UnconstrainedFunctionBroadcastedEvent, } from '@aztec/protocol-contracts/class-registerer'; -import { ContractInstanceDeployedEvent } from '@aztec/protocol-contracts/instance-deployer'; +import { + ContractInstanceDeployedEvent, + ContractInstanceUpdatedEvent, +} from '@aztec/protocol-contracts/instance-deployer'; import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client'; import groupBy from 'lodash.groupby'; @@ -688,9 +692,11 @@ export class Archiver implements ArchiveSource, Traceable { if (!instance) { throw new Error(`Contract ${address.toString()} not found`); } - const contractClass = await this.getContractClass(instance.contractClassId); + const contractClass = await this.getContractClass(instance.currentContractClassId); if (!contractClass) { - throw new Error(`Contract class ${instance.contractClassId.toString()} for ${address.toString()} not found`); + throw new Error( + `Contract class ${instance.currentContractClassId.toString()} for ${address.toString()} not found`, + ); } return contractClass.publicFunctions.find(f => f.selector.equals(selector)); } @@ -872,6 +878,8 @@ class ArchiverStoreHelper | 'deleteContractClasses' | 'addContractInstances' | 'deleteContractInstances' + | 'addContractInstanceUpdates' + | 'deleteContractInstanceUpdates' | 'addFunctions' > { @@ -935,6 +943,29 @@ class ArchiverStoreHelper return true; } + /** + * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract. + * @param allLogs - All logs emitted in a bunch of blocks. + */ + async #updateUpdatedContractInstances(allLogs: PublicLog[], blockNum: number, operation: Operation) { + const contractUpdates = allLogs + .filter(log => ContractInstanceUpdatedEvent.isContractInstanceUpdatedEvent(log)) + .map(log => ContractInstanceUpdatedEvent.fromLog(log)) + .map(e => e.toContractInstanceUpdate()); + + if (contractUpdates.length > 0) { + contractUpdates.forEach(c => + this.#log.verbose(`${Operation[operation]} contract instance update at ${c.address.toString()}`), + ); + if (operation == Operation.Store) { + return await this.store.addContractInstanceUpdates(contractUpdates, blockNum); + } else if (operation == Operation.Delete) { + return await this.store.deleteContractInstanceUpdates(contractUpdates, blockNum); + } + } + return true; + } + /** * Stores the functions that was broadcasted individually * @@ -1009,10 +1040,12 @@ class ArchiverStoreHelper .flatMap(txLog => txLog.unrollLogs()); // ContractInstanceDeployed event logs are broadcast in privateLogs. const privateLogs = block.data.body.txEffects.flatMap(txEffect => txEffect.privateLogs); + const publicLogs = block.data.body.txEffects.flatMap(txEffect => txEffect.publicLogs); return ( await Promise.all([ this.#updateRegisteredContractClasses(contractClassLogs, block.data.number, Operation.Store), this.#updateDeployedContractInstances(privateLogs, block.data.number, Operation.Store), + this.#updateUpdatedContractInstances(publicLogs, block.data.number, Operation.Store), this.#storeBroadcastedIndividualFunctions(contractClassLogs, block.data.number), ]) ).every(Boolean); @@ -1042,11 +1075,13 @@ class ArchiverStoreHelper // ContractInstanceDeployed event logs are broadcast in privateLogs. const privateLogs = block.data.body.txEffects.flatMap(txEffect => txEffect.privateLogs); + const publicLogs = block.data.body.txEffects.flatMap(txEffect => txEffect.publicLogs); return ( await Promise.all([ this.#updateRegisteredContractClasses(contractClassLogs, block.data.number, Operation.Delete), this.#updateDeployedContractInstances(privateLogs, block.data.number, Operation.Delete), + this.#updateUpdatedContractInstances(publicLogs, block.data.number, Operation.Delete), ]) ).every(Boolean); }), diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 30364404e1c..95e32409542 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -13,6 +13,7 @@ import { import { type BlockHeader, type ContractClassPublic, + type ContractInstanceUpdateWithAddress, type ContractInstanceWithAddress, type ExecutablePrivateFunctionWithMembershipProof, type Fr, @@ -244,6 +245,14 @@ export interface ArchiverDataStore { addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise; deleteContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise; + /** + * Add new contract instance updates + * @param data - List of contract updates to be added. + * @param blockNumber - Number of the L2 block the updates were scheduled in. + * @returns True if the operation is successful. + */ + addContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], blockNumber: number): Promise; + deleteContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], blockNumber: number): Promise; /** * Adds private functions to a contract class. */ @@ -254,7 +263,7 @@ export interface ArchiverDataStore { ): Promise; /** - * Returns a contract instance given its address, or undefined if not exists. + * Returns a contract instance given its address and the given block number, or undefined if not exists. * @param address - Address of the contract. */ getContractInstance(address: AztecAddress): Promise; diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index dea8335c536..39df377b939 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -279,7 +279,11 @@ export function describeArchiverDataStore( const blockNum = 10; beforeEach(async () => { - const randomInstance = await SerializableContractInstance.random(); + const classId = Fr.random(); + const randomInstance = await SerializableContractInstance.random({ + currentContractClassId: classId, + originalContractClassId: classId, + }); contractInstance = { ...randomInstance, address: await AztecAddress.random() }; await store.addContractInstances([contractInstance], blockNum); }); diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts index 4e1818f7e24..17c7ceeded3 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts @@ -1,14 +1,25 @@ -import { type AztecAddress, type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/circuits.js'; +import { + type AztecAddress, + type ContractInstanceUpdateWithAddress, + type ContractInstanceWithAddress, + type Fr, + SerializableContractInstance, + SerializableContractInstanceUpdate, +} from '@aztec/circuits.js'; import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store'; +type ContractInstanceUpdateKey = [string, number] | [string, number, number]; + /** * LMDB implementation of the ArchiverDataStore interface. */ export class ContractInstanceStore { #contractInstances: AztecAsyncMap; + #contractInstanceUpdates: AztecAsyncMap; constructor(db: AztecAsyncKVStore) { this.#contractInstances = db.openMap('archiver_contract_instances'); + this.#contractInstanceUpdates = db.openMap('archiver_contract_instance_updates'); } addContractInstance(contractInstance: ContractInstanceWithAddress): Promise { @@ -22,8 +33,75 @@ export class ContractInstanceStore { return this.#contractInstances.delete(contractInstance.address.toString()); } - async getContractInstance(address: AztecAddress): Promise { + getUpdateKey(contractAddress: AztecAddress, blockNumber: number, logIndex?: number): ContractInstanceUpdateKey { + if (logIndex === undefined) { + return [contractAddress.toString(), blockNumber]; + } else { + return [contractAddress.toString(), blockNumber, logIndex]; + } + } + + addContractInstanceUpdate( + contractInstanceUpdate: ContractInstanceUpdateWithAddress, + blockNumber: number, + logIndex: number, + ): Promise { + return this.#contractInstanceUpdates.set( + this.getUpdateKey(contractInstanceUpdate.address, blockNumber, logIndex), + new SerializableContractInstanceUpdate(contractInstanceUpdate).toBuffer(), + ); + } + + deleteContractInstanceUpdate( + contractInstanceUpdate: ContractInstanceUpdateWithAddress, + blockNumber: number, + logIndex: number, + ): Promise { + return this.#contractInstanceUpdates.delete( + this.getUpdateKey(contractInstanceUpdate.address, blockNumber, logIndex), + ); + } + + async getCurrentContractInstanceClassId( + address: AztecAddress, + blockNumber: number, + originalClassId: Fr, + ): Promise { + // We need to find the last update before the given block number + const queryResult = await this.#contractInstanceUpdates + .valuesAsync({ + reverse: true, + end: this.getUpdateKey(address, blockNumber + 1), // No update can match this key since it doesn't have a log index. We want the highest key <= blockNumber + limit: 1, + }) + .next(); + if (queryResult.done) { + return originalClassId; + } + + const serializedUpdate = queryResult.value; + const update = SerializableContractInstanceUpdate.fromBuffer(serializedUpdate); + if (blockNumber < update.blockOfChange) { + return update.prevContractClassId.isZero() ? originalClassId : update.prevContractClassId; + } + return update.newContractClassId; + } + + async getContractInstance( + address: AztecAddress, + blockNumber: number, + ): Promise { const contractInstance = await this.#contractInstances.getAsync(address.toString()); - return contractInstance && SerializableContractInstance.fromBuffer(contractInstance).withAddress(address); + if (!contractInstance) { + return undefined; + } + + const instance = SerializableContractInstance.fromBuffer(contractInstance).withAddress(address); + instance.currentContractClassId = await this.getCurrentContractInstanceClassId( + address, + blockNumber, + instance.originalContractClassId, + ); + return instance; } } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts index f392a1619cd..8df0355bc90 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts @@ -12,6 +12,7 @@ import { import { type BlockHeader, type ContractClassPublic, + type ContractInstanceUpdateWithAddress, type ContractInstanceWithAddress, type ExecutablePrivateFunctionWithMembershipProof, type Fr, @@ -83,8 +84,8 @@ export class KVArchiverDataStore implements ArchiverDataStore { return this.#contractClassStore.getContractClassIds(); } - getContractInstance(address: AztecAddress): Promise { - const contract = this.#contractInstanceStore.getContractInstance(address); + async getContractInstance(address: AztecAddress): Promise { + const contract = this.#contractInstanceStore.getContractInstance(address, await this.getSynchedL2BlockNumber()); return contract; } @@ -126,6 +127,28 @@ export class KVArchiverDataStore implements ArchiverDataStore { return (await Promise.all(data.map(c => this.#contractInstanceStore.deleteContractInstance(c)))).every(Boolean); } + async addContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], blockNumber: number): Promise { + return ( + await Promise.all( + data.map((update, logIndex) => + this.#contractInstanceStore.addContractInstanceUpdate(update, blockNumber, logIndex), + ), + ) + ).every(Boolean); + } + async deleteContractInstanceUpdates( + data: ContractInstanceUpdateWithAddress[], + blockNumber: number, + ): Promise { + return ( + await Promise.all( + data.map((update, logIndex) => + this.#contractInstanceStore.deleteContractInstanceUpdate(update, blockNumber, logIndex), + ), + ) + ).every(Boolean); + } + /** * Append new blocks to the store's list. * @param blocks - The L2 blocks to be added to the store and the last processed L1 block. diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts index 2ddcbc77a00..c5b0381a38e 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts @@ -20,6 +20,7 @@ import { type BlockHeader, type ContractClassPublic, type ContractClassPublicWithBlockNumber, + type ContractInstanceUpdateWithAddress, type ContractInstanceWithAddress, type ExecutablePrivateFunctionWithMembershipProof, Fr, @@ -40,6 +41,8 @@ import { type DataRetrieval } from '../structs/data_retrieval.js'; import { type L1Published } from '../structs/published.js'; import { L1ToL2MessageStore } from './l1_to_l2_message_store.js'; +type StoredContractInstanceUpdate = ContractInstanceUpdateWithAddress & { blockNumber: number; logIndex: number }; + /** * Simple, in-memory implementation of an archiver data store. */ @@ -81,7 +84,10 @@ export class MemoryArchiverStore implements ArchiverDataStore { private contractInstances: Map = new Map(); + private contractInstanceUpdates: Map = new Map(); + private lastL1BlockNewBlocks: bigint | undefined = undefined; + private lastL1BlockNewMessages: bigint | undefined = undefined; private lastProvenL2BlockNumber: number = 0; @@ -112,7 +118,23 @@ export class MemoryArchiverStore implements ArchiverDataStore { } public getContractInstance(address: AztecAddress): Promise { - return Promise.resolve(this.contractInstances.get(address.toString())); + const instance = this.contractInstances.get(address.toString()); + if (!instance) { + return Promise.resolve(undefined); + } + const updates = this.contractInstanceUpdates.get(address.toString()) || []; + if (updates.length > 0) { + const lastUpdate = updates[0]; + const currentBlockNumber = this.getLastBlockNumber(); + if (currentBlockNumber >= lastUpdate.blockOfChange) { + instance.currentContractClassId = lastUpdate.newContractClassId; + } else if (!lastUpdate.prevContractClassId.isZero()) { + instance.currentContractClassId = lastUpdate.prevContractClassId; + } else { + instance.currentContractClassId = instance.originalContractClassId; + } + } + return Promise.resolve(instance); } public getBytecodeCommitment(contractClassId: Fr): Promise { @@ -186,6 +208,33 @@ export class MemoryArchiverStore implements ArchiverDataStore { return Promise.resolve(true); } + public addContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], blockNumber: number): Promise { + for (let logIndex = 0; logIndex < data.length; logIndex++) { + const contractInstanceUpdate = data[logIndex]; + const updates = this.contractInstanceUpdates.get(contractInstanceUpdate.address.toString()) || []; + updates.unshift({ + ...contractInstanceUpdate, + blockNumber, + logIndex, + }); + this.contractInstanceUpdates.set(contractInstanceUpdate.address.toString(), updates); + } + return Promise.resolve(true); + } + + public deleteContractInstanceUpdates( + data: ContractInstanceUpdateWithAddress[], + blockNumber: number, + ): Promise { + for (let logIndex = 0; logIndex < data.length; logIndex++) { + const contractInstanceUpdate = data[logIndex]; + let updates = this.contractInstanceUpdates.get(contractInstanceUpdate.address.toString()) || []; + updates = updates.filter(update => !(update.blockNumber === blockNumber && update.logIndex === logIndex)); + this.contractInstanceUpdates.set(contractInstanceUpdate.address.toString(), updates); + } + return Promise.resolve(true); + } + /** * Append new blocks to the store's list. * @param blocks - The L2 blocks to be added to the store and the last processed L1 block. @@ -690,15 +739,19 @@ export class MemoryArchiverStore implements ArchiverDataStore { }); } + getLastBlockNumber(): number { + if (this.l2Blocks.length === 0) { + return INITIAL_L2_BLOCK_NUM - 1; + } + return this.l2Blocks[this.l2Blocks.length - 1].data.number; + } + /** * Gets the number of the latest L2 block processed. * @returns The number of the latest L2 block processed. */ public getSynchedL2BlockNumber(): Promise { - if (this.l2Blocks.length === 0) { - return Promise.resolve(INITIAL_L2_BLOCK_NUM - 1); - } - return Promise.resolve(this.l2Blocks[this.l2Blocks.length - 1].data.number); + return Promise.resolve(this.getLastBlockNumber()); } public getProvenL2BlockNumber(): Promise { diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index e8943c10a72..191dff629a1 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -13,6 +13,7 @@ import { EthAddress, GasFees, type NodeInfo, + getContractClassFromArtifact, } from '@aztec/circuits.js'; import { type L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses'; import { type AbiDecoded, type ContractArtifact, FunctionType } from '@aztec/foundation/abi'; @@ -79,14 +80,23 @@ describe('Contract Class', () => { returnTypes: [], errorTypes: {}, bytecode: Buffer.alloc(8, 0xfa), + verificationKey: 'fake-verification-key', }, { - name: 'baz', + name: 'public_dispatch', isInitializer: false, isStatic: false, functionType: FunctionType.PUBLIC, isInternal: false, - parameters: [], + parameters: [ + { + name: 'selector', + type: { + kind: 'field', + }, + visibility: 'public', + }, + ], returnTypes: [], errorTypes: {}, bytecode: Buffer.alloc(8, 0xfb), @@ -131,7 +141,12 @@ describe('Contract Class', () => { beforeEach(async () => { contractAddress = await AztecAddress.random(); account = await CompleteAddress.random(); - contractInstance = { address: contractAddress } as ContractInstanceWithAddress; + const contractClass = await getContractClassFromArtifact(defaultArtifact); + contractInstance = { + address: contractAddress, + currentContractClassId: contractClass.id, + originalContractClassId: contractClass.id, + } as ContractInstanceWithAddress; const mockNodeInfo: NodeInfo = { nodeVersion: 'vx.x.x', diff --git a/yarn-project/aztec.js/src/contract/contract.ts b/yarn-project/aztec.js/src/contract/contract.ts index 6472a9da880..71d61dee8bc 100644 --- a/yarn-project/aztec.js/src/contract/contract.ts +++ b/yarn-project/aztec.js/src/contract/contract.ts @@ -1,4 +1,4 @@ -import { PublicKeys } from '@aztec/circuits.js'; +import { PublicKeys, getContractClassFromArtifact } from '@aztec/circuits.js'; import { type ContractArtifact } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; @@ -26,6 +26,12 @@ export class Contract extends ContractBase { if (instance === undefined) { throw new Error(`Contract instance at ${address.toString()} has not been registered in the wallet's PXE`); } + const thisContractClass = await getContractClassFromArtifact(artifact); + if (!thisContractClass.id.equals(instance.currentContractClassId)) { + // wallet holds an outdated version of this contract + await wallet.updateContract(address, artifact); + instance.currentContractClassId = thisContractClass.id; + } return new Contract(instance, artifact, wallet); } diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index cf3cc693dfe..fc877e049ed 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -145,9 +145,9 @@ export class DeployMethod extends Bas // Obtain contract class from artifact and check it matches the reported one by the instance. // TODO(@spalladino): We're unnecessarily calculating the contract class multiple times here. const contractClass = await getContractClassFromArtifact(this.artifact); - if (!instance.contractClassId.equals(contractClass.id)) { + if (!instance.currentContractClassId.equals(contractClass.id)) { throw new Error( - `Contract class mismatch when deploying contract: got ${instance.contractClassId.toString()} from instance and ${contractClass.id.toString()} from artifact`, + `Contract class mismatch when deploying contract: got ${instance.currentContractClassId.toString()} from instance and ${contractClass.id.toString()} from artifact`, ); } diff --git a/yarn-project/aztec.js/src/deployment/deploy_instance.ts b/yarn-project/aztec.js/src/deployment/deploy_instance.ts index bf0d501d28d..4b49fbbfc8c 100644 --- a/yarn-project/aztec.js/src/deployment/deploy_instance.ts +++ b/yarn-project/aztec.js/src/deployment/deploy_instance.ts @@ -14,7 +14,7 @@ export async function deployInstance( instance: ContractInstanceWithAddress, ): Promise { const deployerContract = await getDeployerContract(wallet); - const { salt, contractClassId, publicKeys, deployer } = instance; + const { salt, currentContractClassId: contractClassId, publicKeys, deployer } = instance; const isUniversalDeploy = deployer.isZero(); if (!isUniversalDeploy && !wallet.getAddress().equals(deployer)) { throw new Error( diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index c8cdbe034c6..e6c1fe96ab0 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -94,6 +94,9 @@ export abstract class BaseWallet implements Wallet { registerContractClass(artifact: ContractArtifact): Promise { return this.pxe.registerContractClass(artifact); } + updateContract(contractAddress: AztecAddress, artifact: ContractArtifact): Promise { + return this.pxe.updateContract(contractAddress, artifact); + } getContracts(): Promise { return this.pxe.getContracts(); } diff --git a/yarn-project/aztec/src/cli/cmds/start_pxe.ts b/yarn-project/aztec/src/cli/cmds/start_pxe.ts index 6254e2ee194..1fcd5135cf2 100644 --- a/yarn-project/aztec/src/cli/cmds/start_pxe.ts +++ b/yarn-project/aztec/src/cli/cmds/start_pxe.ts @@ -102,13 +102,15 @@ export async function addPXE( await Promise.all( Object.values(l2Contracts).map(async ({ name, address, artifact, initHash, salt }) => { + const contractClass = await getContractClassFromArtifact(artifact!); const instance: ContractInstanceWithAddress = { version: 1, salt, initializationHash: initHash, address, deployer: AztecAddress.ZERO, - contractClassId: (await getContractClassFromArtifact(artifact!)).id, + currentContractClassId: contractClass.id, + originalContractClassId: contractClass.id, publicKeys: PublicKeys.default(), }; userLog(`Registering ${name} at ${address.toString()}`); diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_class_limits.test.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_class_limits.test.ts index bbba6b94cb4..98daadbc0a8 100644 --- a/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_class_limits.test.ts +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_class_limits.test.ts @@ -44,7 +44,7 @@ describe('AVM WitGen & Circuit – check circuit - contract class limits', () => // include another contract address that reuses a class ID to ensure that we can call it even after the limit is reached const instanceSameClassAsFirstContract = await makeContractInstanceFromClassId( - instances[0].contractClassId, + instances[0].currentContractClassId, /*seed=*/ 1000, ); instanceAddresses.push(instanceSameClassAsFirstContract.address); diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_updates.test.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_updates.test.ts new file mode 100644 index 00000000000..4328f0d06f9 --- /dev/null +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_contract_updates.test.ts @@ -0,0 +1,207 @@ +import { + AztecAddress, + type ContractClassPublic, + type ContractInstanceWithAddress, + FunctionSelector, + PUBLIC_DISPATCH_SELECTOR, + ScheduledDelayChange, + ScheduledValueChange, + UPDATED_CLASS_IDS_SLOT, + computeSharedMutableHashSlot, +} from '@aztec/circuits.js'; +import { deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; +import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/circuits.js/testing'; +import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; +import { DEFAULT_BLOCK_NUMBER, getAvmTestContractBytecode } from '@aztec/simulator/public/fixtures'; + +import { AvmProvingTester } from './avm_proving_tester.js'; + +const TIMEOUT = 300_000; +const DISPATCH_FN_NAME = 'public_dispatch'; +const DISPATCH_SELECTOR = new FunctionSelector(PUBLIC_DISPATCH_SELECTOR); + +describe('AVM WitGen & Circuit - contract updates', () => { + const sender = AztecAddress.fromNumber(42); + + const avmTestContractClassSeed = 0; + const avmTestContractBytecode = getAvmTestContractBytecode(DISPATCH_FN_NAME); + let avmTestContractClass: ContractClassPublic; + let avmTestContractInstance: ContractInstanceWithAddress; + + beforeEach(async () => { + avmTestContractClass = await makeContractClassPublic( + /*seed=*/ avmTestContractClassSeed, + /*publicDispatchFunction=*/ { bytecode: avmTestContractBytecode, selector: DISPATCH_SELECTOR }, + ); + }); + + const writeContractUpdate = async ( + tester: AvmProvingTester, + contractAddress: AztecAddress, + previousClassId: Fr, + nextClassId: Fr, + blockOfChange: number, + ) => { + const sharedMutableSlot = await deriveStorageSlotInMap(new Fr(UPDATED_CLASS_IDS_SLOT), contractAddress); + + const valueChange = new ScheduledValueChange(previousClassId, nextClassId, blockOfChange); + const delayChange = ScheduledDelayChange.empty(); + const writeToTree = async (storageSlot: Fr, value: Fr) => { + await tester.setPublicStorage(ProtocolContractAddress.ContractInstanceDeployer, storageSlot, value); + }; + await valueChange.writeToTree(sharedMutableSlot, writeToTree); + await delayChange.writeToTree(sharedMutableSlot, writeToTree); + + const updatePreimage = [...valueChange.toFields(), delayChange.toField()]; + const updateHash = await poseidon2Hash(updatePreimage); + + const hashSlot = await computeSharedMutableHashSlot(sharedMutableSlot); + + await writeToTree(hashSlot, updateHash); + }; + + it( + 'should execute an updated contract', + async () => { + // Contract was not originally the avmTestContract + const originalClassId = new Fr(27); + avmTestContractInstance = await makeContractInstanceFromClassId( + originalClassId, + /*seed=*/ avmTestContractClassSeed, + { + currentClassId: avmTestContractClass.id, + }, + ); + const tester = await AvmProvingTester.create(/*checkCircuitOnly*/ true); + await tester.addContractClass(avmTestContractClass, AvmTestContractArtifact); + await tester.addContractInstance(avmTestContractInstance); + await writeContractUpdate( + tester, + avmTestContractInstance.address, + avmTestContractInstance.originalContractClassId, + avmTestContractInstance.currentContractClassId, + DEFAULT_BLOCK_NUMBER, + ); + + await tester.simProveVerify( + sender, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { address: avmTestContractInstance.address, fnName: 'add_args_return', args: [new Fr(1), new Fr(2)] }, + ], + /*teardownCall=*/ undefined, + /*expectRevert=*/ false, + ); + }, + TIMEOUT, + ); + + it( + 'should fail if trying to execute an updated contract which has not been updated yet', + async () => { + // Contract was not originally the avmTestContract + const originalClassId = new Fr(27); + avmTestContractInstance = await makeContractInstanceFromClassId( + originalClassId, + /*seed=*/ avmTestContractClassSeed, + { + currentClassId: avmTestContractClass.id, + }, + ); + const tester = await AvmProvingTester.create(/*checkCircuitOnly*/ true); + await tester.addContractClass(avmTestContractClass, AvmTestContractArtifact); + await tester.addContractInstance(avmTestContractInstance); + await writeContractUpdate( + tester, + avmTestContractInstance.address, + avmTestContractInstance.originalContractClassId, + avmTestContractInstance.currentContractClassId, + DEFAULT_BLOCK_NUMBER + 1, + ); + + await expect( + tester.simProveVerify( + sender, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { address: avmTestContractInstance.address, fnName: 'add_args_return', args: [new Fr(1), new Fr(2)] }, + ], + /*teardownCall=*/ undefined, + /*expectRevert=*/ false, + ), + ).rejects.toThrow(); + }, + TIMEOUT, + ); + + it( + 'should execute a not yet updated contract', + async () => { + // Contract was not originally the avmTestContract + const newClassId = new Fr(27); + avmTestContractInstance = await makeContractInstanceFromClassId( + avmTestContractClass.id, + /*seed=*/ avmTestContractClassSeed, + ); + const tester = await AvmProvingTester.create(/*checkCircuitOnly*/ true); + await tester.addContractClass(avmTestContractClass, AvmTestContractArtifact); + await tester.addContractInstance(avmTestContractInstance); + await writeContractUpdate( + tester, + avmTestContractInstance.address, + avmTestContractInstance.currentContractClassId, + newClassId, + DEFAULT_BLOCK_NUMBER + 1, + ); + + await tester.simProveVerify( + sender, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { address: avmTestContractInstance.address, fnName: 'add_args_return', args: [new Fr(1), new Fr(2)] }, + ], + /*teardownCall=*/ undefined, + /*expectRevert=*/ false, + ); + }, + TIMEOUT, + ); + + it( + 'should fail if trying to execute the old class of a contract which has been updated already', + async () => { + // Contract was not originally the avmTestContract + const newClassId = new Fr(27); + avmTestContractInstance = await makeContractInstanceFromClassId( + avmTestContractClass.id, + /*seed=*/ avmTestContractClassSeed, + ); + const tester = await AvmProvingTester.create(/*checkCircuitOnly*/ true); + await tester.addContractClass(avmTestContractClass, AvmTestContractArtifact); + await tester.addContractInstance(avmTestContractInstance); + await writeContractUpdate( + tester, + avmTestContractInstance.address, + avmTestContractInstance.currentContractClassId, + newClassId, + DEFAULT_BLOCK_NUMBER - 1, + ); + + await expect( + tester.simProveVerify( + sender, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { address: avmTestContractInstance.address, fnName: 'add_args_return', args: [new Fr(1), new Fr(2)] }, + ], + /*teardownCall=*/ undefined, + /*expectRevert=*/ false, + ), + ).rejects.toThrow(); + }, + TIMEOUT, + ); +}); diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_and_verification.test.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_and_verification.test.ts index 56656363080..73298d5b975 100644 --- a/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_and_verification.test.ts +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_proving_and_verification.test.ts @@ -49,7 +49,7 @@ describe('AVM WitGen & Circuit – proving and verification', () => { argsU8, /*getInstanceForAddress=*/ expectContractInstance.address.toField(), /*expectedDeployer=*/ expectContractInstance.deployer.toField(), - /*expectedClassId=*/ expectContractInstance.contractClassId.toField(), + /*expectedClassId=*/ expectContractInstance.currentContractClassId.toField(), /*expectedInitializationHash=*/ expectContractInstance.initializationHash.toField(), ]; diff --git a/yarn-project/bb-prover/src/avm_proving_tests/avm_v2.test.ts b/yarn-project/bb-prover/src/avm_proving_tests/avm_v2.test.ts index 1ba8f7658b1..81901f2f917 100644 --- a/yarn-project/bb-prover/src/avm_proving_tests/avm_v2.test.ts +++ b/yarn-project/bb-prover/src/avm_proving_tests/avm_v2.test.ts @@ -49,7 +49,7 @@ describe('AVM v2', () => { argsU8, /*getInstanceForAddress=*/ expectContractInstance.address.toField(), /*expectedDeployer=*/ expectContractInstance.deployer.toField(), - /*expectedClassId=*/ expectContractInstance.contractClassId.toField(), + /*expectedClassId=*/ expectContractInstance.currentContractClassId.toField(), /*expectedInitializationHash=*/ expectContractInstance.initializationHash.toField(), ]; diff --git a/yarn-project/circuit-types/src/interfaces/archiver.test.ts b/yarn-project/circuit-types/src/interfaces/archiver.test.ts index 7146dd87722..d5e2bd76d41 100644 --- a/yarn-project/circuit-types/src/interfaces/archiver.test.ts +++ b/yarn-project/circuit-types/src/interfaces/archiver.test.ts @@ -237,7 +237,8 @@ describe('ArchiverApiSchema', () => { const result = await context.client.getContract(address); expect(result).toEqual({ address, - contractClassId: expect.any(Fr), + currentContractClassId: expect.any(Fr), + originalContractClassId: expect.any(Fr), deployer: expect.any(AztecAddress), initializationHash: expect.any(Fr), publicKeys: expect.any(PublicKeys), @@ -370,7 +371,8 @@ class MockArchiver implements ArchiverApi { async getContract(address: AztecAddress): Promise { return { address, - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), deployer: await AztecAddress.random(), initializationHash: Fr.random(), publicKeys: await PublicKeys.random(), diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts index 44d52d36ca9..9d2fbbe7af4 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.test.ts @@ -329,7 +329,8 @@ describe('AztecNodeApiSchema', () => { const response = await context.client.getContract(await AztecAddress.random()); expect(response).toEqual({ address: expect.any(AztecAddress), - contractClassId: expect.any(Fr), + currentContractClassId: expect.any(Fr), + originalContractClassId: expect.any(Fr), deployer: expect.any(AztecAddress), initializationHash: expect.any(Fr), publicKeys: expect.any(PublicKeys), @@ -597,7 +598,8 @@ class MockAztecNode implements AztecNode { expect(address).toBeInstanceOf(AztecAddress); const instance = { version: 1 as const, - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), deployer: await AztecAddress.random(), initializationHash: Fr.random(), publicKeys: await PublicKeys.random(), diff --git a/yarn-project/circuit-types/src/interfaces/pxe.test.ts b/yarn-project/circuit-types/src/interfaces/pxe.test.ts index def4889fc4d..4d5decc2714 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.test.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.test.ts @@ -77,7 +77,8 @@ describe('PXESchema', () => { address = await AztecAddress.random(); instance = { version: 1, - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), deployer: await AztecAddress.random(), initializationHash: Fr.random(), publicKeys: await PublicKeys.random(), @@ -147,6 +148,10 @@ describe('PXESchema', () => { await context.client.registerContract({ instance, artifact }); }); + it('updateContract', async () => { + await context.client.updateContract(instance.address, artifact); + }); + it('getContracts', async () => { const result = await context.client.getContracts(); expect(result).toEqual([address]); @@ -383,6 +388,10 @@ class MockPXE implements PXE { deepStrictEqual(contract.artifact, this.artifact); return Promise.resolve(); } + updateContract(contractAddress: AztecAddress, _artifact: ContractArtifact): Promise { + expect(contractAddress).toEqual(this.address); + return Promise.resolve(); + } getContracts(): Promise { return Promise.resolve([this.address]); } diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index 5fe85fe0843..1d58083a2cc 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -146,6 +146,15 @@ export interface PXE { */ registerContract(contract: { instance: ContractInstanceWithAddress; artifact?: ContractArtifact }): Promise; + /** + * Updates a deployed contract in the PXE Service. This is used to update the contract artifact when + * an update has happened, so the new code can be used in the simulation of local transactions. + * This is called by aztec.js when instantiating a contract in a given address with a mismatching artifact. + * @param contractAddress - The address of the contract to update. + * @param artifact - The updated artifact for the contract. + */ + updateContract(contractAddress: AztecAddress, artifact: ContractArtifact): Promise; + /** * Retrieves the addresses of contracts added to this PXE Service. * @returns An array of contracts addresses registered on this PXE Service. @@ -475,6 +484,7 @@ export const PXESchema: ApiSchemaFor = { .function() .args(z.object({ instance: ContractInstanceWithAddressSchema, artifact: z.optional(ContractArtifactSchema) })) .returns(z.void()), + updateContract: z.function().args(schemas.AztecAddress, ContractArtifactSchema).returns(z.void()), getContracts: z.function().returns(z.array(schemas.AztecAddress)), proveTx: z.function().args(TxExecutionRequest.schema, PrivateExecutionResult.schema).returns(TxProvingResult.schema), simulateTx: z diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 79646dbd34b..21b60da07ff 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -220,7 +220,14 @@ export const randomContractInstanceWithAddress = async ( opts: { contractClassId?: Fr } = {}, address?: AztecAddress, ): Promise => { - const instance = await SerializableContractInstance.random(opts); + const instance = await SerializableContractInstance.random( + opts.contractClassId + ? { + currentContractClassId: opts.contractClassId, + originalContractClassId: opts.contractClassId, + } + : undefined, + ); return instance.withAddress(address ?? (await computeContractAddressFromInstance(instance))); }; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index ad3ade12cf0..e3ddd8f3997 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -99,6 +99,8 @@ export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 24399338136397901754495080759185489776044879232766421623673792970137n; export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 14061769416655647708490531650437236735160113654556896985372298487345n; +export const DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE = + 1534834688047131268740281708431107902615560100979874281215533519862n; export const MAX_PROTOCOL_CONTRACTS = 7; export const CANONICAL_AUTH_REGISTRY_ADDRESS = 1; export const DEPLOYER_CONTRACT_ADDRESS = 2; @@ -107,6 +109,10 @@ export const MULTI_CALL_ENTRYPOINT_ADDRESS = 4; export const FEE_JUICE_ADDRESS = 5; export const ROUTER_ADDRESS = 6; export const FEE_JUICE_BALANCES_SLOT = 1; +export const UPDATED_CLASS_IDS_SLOT = 1; +export const SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR = 0; +export const SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR = 1; +export const SHARED_MUTABLE_HASH_SEPARATOR = 2; export const DEFAULT_NPK_M_X = 582240093077765400562621227108555700500271598878376310175765873770292988861n; export const DEFAULT_NPK_M_Y = 10422444662424639723529825114205836958711284159673861467999592572974769103684n; export const DEFAULT_IVPK_M_X = 339708709767762472786445938838804872781183545349360029270386718856175781484n; @@ -349,6 +355,7 @@ export const PROOF_TYPE_AVM = 4; export const PROOF_TYPE_ROLLUP_HONK = 5; export const PROOF_TYPE_ROOT_ROLLUP_HONK = 6; export const TWO_POW_64 = 18446744073709551616n; +export const DEFAULT_UPDATE_DELAY = 10; export enum GeneratorIndex { NOTE_HASH = 1, NOTE_HASH_NONCE = 2, diff --git a/yarn-project/circuits.js/src/contract/contract_address.test.ts b/yarn-project/circuits.js/src/contract/contract_address.test.ts index 6eb036136df..7179cd706ab 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.test.ts @@ -14,7 +14,7 @@ describe('ContractAddress', () => { setupCustomSnapshotSerializers(expect); it('computePartialAddress', async () => { const mockInstance = { - contractClassId: new Fr(1), + originalContractClassId: new Fr(1), saltedInitializationHash: new Fr(2), }; const result = await computePartialAddress(mockInstance); @@ -64,7 +64,8 @@ describe('ContractAddress', () => { await computeContractAddressFromInstance({ publicKeys, salt, - contractClassId, + originalContractClassId: contractClassId, + currentContractClassId: contractClassId, initializationHash, deployer, version: 1, diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts index ebaeadb2d68..98a577774d6 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -22,7 +22,7 @@ import { type ContractInstance } from './interfaces/contract_instance.js'; export async function computeContractAddressFromInstance( instance: | ContractInstance - | ({ contractClassId: Fr; saltedInitializationHash: Fr } & Pick), + | ({ originalContractClassId: Fr; saltedInitializationHash: Fr } & Pick), ): Promise { const partialAddress = await computePartialAddress(instance); return computeAddress(instance.publicKeys, partialAddress); @@ -34,8 +34,8 @@ export async function computeContractAddressFromInstance( */ export async function computePartialAddress( instance: - | Pick - | { contractClassId: Fr; saltedInitializationHash: Fr }, + | Pick + | { originalContractClassId: Fr; saltedInitializationHash: Fr }, ): Promise { const saltedInitializationHash = 'saltedInitializationHash' in instance @@ -43,7 +43,7 @@ export async function computePartialAddress( : await computeSaltedInitializationHash(instance); return poseidon2HashWithSeparator( - [instance.contractClassId, saltedInitializationHash], + [instance.originalContractClassId, saltedInitializationHash], GeneratorIndex.PARTIAL_ADDRESS, ); } diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts index a64e7e12fa6..746cf2c1f14 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -25,7 +25,8 @@ export class SerializableContractInstance { public readonly version = VERSION; public readonly salt: Fr; public readonly deployer: AztecAddress; - public readonly contractClassId: Fr; + public readonly currentContractClassId: Fr; + public readonly originalContractClassId: Fr; public readonly initializationHash: Fr; public readonly publicKeys: PublicKeys; @@ -35,7 +36,8 @@ export class SerializableContractInstance { } this.salt = instance.salt; this.deployer = instance.deployer; - this.contractClassId = instance.contractClassId; + this.currentContractClassId = instance.currentContractClassId; + this.originalContractClassId = instance.originalContractClassId; this.initializationHash = instance.initializationHash; this.publicKeys = instance.publicKeys; } @@ -45,7 +47,8 @@ export class SerializableContractInstance { numToUInt8(this.version), this.salt, this.deployer, - this.contractClassId, + this.currentContractClassId, + this.originalContractClassId, this.initializationHash, this.publicKeys, ); @@ -62,7 +65,8 @@ export class SerializableContractInstance { version: reader.readUInt8() as typeof VERSION, salt: reader.readObject(Fr), deployer: reader.readObject(AztecAddress), - contractClassId: reader.readObject(Fr), + currentContractClassId: reader.readObject(Fr), + originalContractClassId: reader.readObject(Fr), initializationHash: reader.readObject(Fr), publicKeys: reader.readObject(PublicKeys), }); @@ -73,7 +77,8 @@ export class SerializableContractInstance { version: VERSION, salt: Fr.random(), deployer: await AztecAddress.random(), - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), initializationHash: Fr.random(), publicKeys: await PublicKeys.random(), ...opts, @@ -85,7 +90,8 @@ export class SerializableContractInstance { version: VERSION, salt: Fr.zero(), deployer: AztecAddress.zero(), - contractClassId: Fr.zero(), + currentContractClassId: Fr.zero(), + originalContractClassId: Fr.zero(), initializationHash: Fr.zero(), publicKeys: PublicKeys.default(), }); @@ -125,7 +131,8 @@ export async function getContractInstanceFromDeployParams( const publicKeys = opts.publicKeys ?? PublicKeys.default(); const instance: ContractInstance = { - contractClassId, + currentContractClassId: contractClassId, + originalContractClassId: contractClassId, initializationHash, publicKeys, salt, diff --git a/yarn-project/circuits.js/src/contract/contract_instance_update.test.ts b/yarn-project/circuits.js/src/contract/contract_instance_update.test.ts new file mode 100644 index 00000000000..1fe3f745fac --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_instance_update.test.ts @@ -0,0 +1,8 @@ +import { SerializableContractInstanceUpdate } from './contract_instance_update.js'; + +describe('ContractInstance', () => { + it('can serialize and deserialize an instance update', () => { + const instance = SerializableContractInstanceUpdate.random(); + expect(SerializableContractInstanceUpdate.fromBuffer(instance.toBuffer())).toEqual(instance); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/contract_instance_update.ts b/yarn-project/circuits.js/src/contract/contract_instance_update.ts new file mode 100644 index 00000000000..f9f8878653a --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_instance_update.ts @@ -0,0 +1,47 @@ +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { type FieldsOf } from '@aztec/foundation/types'; + +import { type ContractInstanceUpdate } from './interfaces/contract_instance_update.js'; + +export class SerializableContractInstanceUpdate { + prevContractClassId: Fr; + newContractClassId: Fr; + blockOfChange: number; + + constructor(instance: ContractInstanceUpdate) { + this.prevContractClassId = instance.prevContractClassId; + this.newContractClassId = instance.newContractClassId; + this.blockOfChange = instance.blockOfChange; + } + + public toBuffer() { + return serializeToBuffer(this.prevContractClassId, this.newContractClassId, this.blockOfChange); + } + + static fromBuffer(bufferOrReader: Buffer | BufferReader) { + const reader = BufferReader.asReader(bufferOrReader); + return new SerializableContractInstanceUpdate({ + prevContractClassId: reader.readObject(Fr), + newContractClassId: reader.readObject(Fr), + blockOfChange: reader.readNumber(), + }); + } + + static random(opts: Partial> = {}) { + return new SerializableContractInstanceUpdate({ + prevContractClassId: Fr.random(), + newContractClassId: Fr.random(), + blockOfChange: Math.floor(Math.random() * 1000), + ...opts, + }); + } + + static default() { + return new SerializableContractInstanceUpdate({ + prevContractClassId: Fr.zero(), + newContractClassId: Fr.zero(), + blockOfChange: 0, + }); + } +} diff --git a/yarn-project/circuits.js/src/contract/index.ts b/yarn-project/circuits.js/src/contract/index.ts index 7376d76d662..46e26b11748 100644 --- a/yarn-project/circuits.js/src/contract/index.ts +++ b/yarn-project/circuits.js/src/contract/index.ts @@ -3,6 +3,7 @@ export * from './contract_address.js'; export * from './contract_class.js'; export * from './contract_class_id.js'; export * from './contract_instance.js'; +export * from './contract_instance_update.js'; export * from './private_function.js'; export * from './private_function_membership_proof.js'; export * from './unconstrained_function_membership_proof.js'; diff --git a/yarn-project/circuits.js/src/contract/interfaces/contract_instance.ts b/yarn-project/circuits.js/src/contract/interfaces/contract_instance.ts index 0d4d0092b8b..31d66372284 100644 --- a/yarn-project/circuits.js/src/contract/interfaces/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/interfaces/contract_instance.ts @@ -21,7 +21,9 @@ export interface ContractInstance { /** Optional deployer address or zero if this was a universal deploy. */ deployer: AztecAddress; /** Identifier of the contract class for this instance. */ - contractClassId: Fr; + currentContractClassId: Fr; + /** Identifier of the original (at deployment) contract class for this instance */ + originalContractClassId: Fr; /** Hash of the selector and arguments to the constructor. */ initializationHash: Fr; /** Public keys associated with this instance. */ @@ -34,7 +36,8 @@ export const ContractInstanceSchema = z.object({ version: z.literal(VERSION), salt: schemas.Fr, deployer: schemas.AztecAddress, - contractClassId: schemas.Fr, + currentContractClassId: schemas.Fr, + originalContractClassId: schemas.Fr, initializationHash: schemas.Fr, publicKeys: PublicKeys.schema, }) satisfies ZodFor; diff --git a/yarn-project/circuits.js/src/contract/interfaces/contract_instance_update.ts b/yarn-project/circuits.js/src/contract/interfaces/contract_instance_update.ts new file mode 100644 index 00000000000..55d0abee6d3 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/interfaces/contract_instance_update.ts @@ -0,0 +1,29 @@ +import { type AztecAddress } from '@aztec/foundation/aztec-address'; +import { type Fr } from '@aztec/foundation/fields'; +import { type ZodFor, schemas } from '@aztec/foundation/schemas'; + +import { z } from 'zod'; + +/** + * An update to a contract instance, changing its contract class. + */ +export interface ContractInstanceUpdate { + /** Identifier of the previous contract class for this instance */ + prevContractClassId: Fr; + /** Identifier of the new contract class for this instance. */ + newContractClassId: Fr; + /** The block number where the contract class in use will be the new one */ + blockOfChange: number; +} + +export type ContractInstanceUpdateWithAddress = ContractInstanceUpdate & { address: AztecAddress }; + +export const ContractInstanceUpdateSchema = z.object({ + prevContractClassId: schemas.Fr, + newContractClassId: schemas.Fr, + blockOfChange: z.number(), +}) satisfies ZodFor; + +export const ContractInstanceUpdateWithAddressSchema = ContractInstanceUpdateSchema.and( + z.object({ address: schemas.AztecAddress }), +) satisfies ZodFor; diff --git a/yarn-project/circuits.js/src/contract/interfaces/index.ts b/yarn-project/circuits.js/src/contract/interfaces/index.ts index e3b0d5ebf86..ab09b853377 100644 --- a/yarn-project/circuits.js/src/contract/interfaces/index.ts +++ b/yarn-project/circuits.js/src/contract/interfaces/index.ts @@ -1,5 +1,6 @@ export * from './contract_class.js'; export * from './contract_data_source.js'; export * from './contract_instance.js'; +export * from './contract_instance_update.js'; export * from './node-info.js'; export * from './protocol_contract_addresses.js'; diff --git a/yarn-project/circuits.js/src/scripts/constants.in.ts b/yarn-project/circuits.js/src/scripts/constants.in.ts index 419daa59e69..60309a33e43 100644 --- a/yarn-project/circuits.js/src/scripts/constants.in.ts +++ b/yarn-project/circuits.js/src/scripts/constants.in.ts @@ -92,6 +92,10 @@ const CPP_CONSTANTS = [ 'ROUTER_ADDRESS', 'FEE_JUICE_BALANCES_SLOT', 'MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS', + 'UPDATED_CLASS_IDS_SLOT', + 'SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR', + 'SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR', + 'SHARED_MUTABLE_HASH_SEPARATOR', ]; const CPP_GENERATORS: string[] = [ diff --git a/yarn-project/circuits.js/src/structs/avm/avm.test.ts b/yarn-project/circuits.js/src/structs/avm/avm.test.ts index ced61219859..8599c0da103 100644 --- a/yarn-project/circuits.js/src/structs/avm/avm.test.ts +++ b/yarn-project/circuits.js/src/structs/avm/avm.test.ts @@ -21,7 +21,8 @@ describe('Avm circuit inputs', () => { exists: true, salt: new Fr(0xdeadbeefn), deployer: AztecAddress.fromBigInt(0x000010n), - contractClassId: new Fr(0x41181337n), + currentContractClassId: new Fr(0x41181337n), + originalContractClassId: new Fr(0x41181337n), initializationHash: new Fr(0x111111n), publicKeys: new PublicKeys( new Point( @@ -52,7 +53,8 @@ describe('Avm circuit inputs', () => { exists: false, salt: new Fr(0xdead0000n), deployer: AztecAddress.fromBigInt(0x000020n), - contractClassId: new Fr(0x51181337n), + currentContractClassId: new Fr(0x51181337n), + originalContractClassId: new Fr(0x51181337n), initializationHash: new Fr(0x222222n), publicKeys: new PublicKeys( new Point( diff --git a/yarn-project/circuits.js/src/structs/avm/avm.ts b/yarn-project/circuits.js/src/structs/avm/avm.ts index 2728525635b..238b1109b8b 100644 --- a/yarn-project/circuits.js/src/structs/avm/avm.ts +++ b/yarn-project/circuits.js/src/structs/avm/avm.ts @@ -86,16 +86,23 @@ export class AvmEnqueuedCallHint { } export class AvmContractInstanceHint { + public readonly updatePreimage: Vector; + constructor( public readonly address: AztecAddress, public readonly exists: boolean, public readonly salt: Fr, public readonly deployer: AztecAddress, - public readonly contractClassId: Fr, + public readonly currentContractClassId: Fr, + public readonly originalContractClassId: Fr, public readonly initializationHash: Fr, public readonly publicKeys: PublicKeys, - public readonly membershipHint: AvmNullifierReadTreeHint = AvmNullifierReadTreeHint.empty(), - ) {} + public readonly initializationMembershipHint: AvmNullifierReadTreeHint = AvmNullifierReadTreeHint.empty(), + public readonly updateMembershipHint: AvmPublicDataReadTreeHint = AvmPublicDataReadTreeHint.empty(), + updatePreimage: Fr[], + ) { + this.updatePreimage = new Vector(updatePreimage); + } /** * Serializes the inputs to a buffer. * @returns - The inputs serialized to a buffer. @@ -122,22 +129,16 @@ export class AvmContractInstanceHint { !this.exists && this.salt.isZero() && this.deployer.isZero() && - this.contractClassId.isZero() && + this.currentContractClassId.isZero() && + this.originalContractClassId.isZero() && this.initializationHash.isZero() && this.publicKeys.isEmpty() && - this.membershipHint.isEmpty() + this.initializationMembershipHint.isEmpty() && + this.updateMembershipHint.isEmpty() && + this.updatePreimage.items.length == 0 ); } - /** - * Creates a new instance from fields. - * @param fields - Fields to create the instance from. - * @returns A new AvmHint instance. - */ - static from(fields: FieldsOf): AvmContractInstanceHint { - return new AvmContractInstanceHint(...AvmContractInstanceHint.getFields(fields)); - } - /** * Extracts fields from an instance. * @param fields - Fields to create the instance from. @@ -149,10 +150,13 @@ export class AvmContractInstanceHint { fields.exists, fields.salt, fields.deployer, - fields.contractClassId, + fields.currentContractClassId, + fields.originalContractClassId, fields.initializationHash, fields.publicKeys, - fields.membershipHint, + fields.initializationMembershipHint, + fields.updateMembershipHint, + fields.updatePreimage, ] as const; } @@ -170,8 +174,11 @@ export class AvmContractInstanceHint { AztecAddress.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), + Fr.fromBuffer(reader), PublicKeys.fromBuffer(reader), AvmNullifierReadTreeHint.fromBuffer(reader), + AvmPublicDataReadTreeHint.fromBuffer(reader), + reader.readVector(Fr), ); } @@ -539,6 +546,10 @@ export class AvmPublicDataReadTreeHint { return this.toBuffer().toString('hex'); } + static empty(): AvmPublicDataReadTreeHint { + return new AvmPublicDataReadTreeHint(PublicDataTreeLeafPreimage.empty(), Fr.ZERO, []); + } + /** * Is the struct empty? * @returns whether all members are empty. diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 64004a2cdb2..f27cd4a22d1 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -70,6 +70,7 @@ export * from './revert_code.js'; export * from './rollup_validation_requests.js'; export * from './scoped_key_validation_request_and_generator.js'; export * from './shared.js'; +export * from './shared_mutable/index.js'; export * from './state_reference.js'; export * from './tree_leaf_read_request.js'; export * from './tree_snapshots.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/private_call_data.ts b/yarn-project/circuits.js/src/structs/kernel/private_call_data.ts index e46fec69cd1..b899852e00f 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_call_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_call_data.ts @@ -2,10 +2,13 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { FUNCTION_TREE_HEIGHT, PROTOCOL_CONTRACT_TREE_HEIGHT } from '../../constants.gen.js'; +import { FUNCTION_TREE_HEIGHT, PROTOCOL_CONTRACT_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT } from '../../constants.gen.js'; import { PublicKeys } from '../../types/public_keys.js'; import { MembershipWitness } from '../membership_witness.js'; import { PrivateCircuitPublicInputs } from '../private_circuit_public_inputs.js'; +import { ScheduledDelayChange } from '../shared_mutable/scheduled_delay_change.js'; +import { ScheduledValueChange } from '../shared_mutable/scheduled_value_change.js'; +import { PublicDataTreeLeafPreimage } from '../trees/public_data_leaf.js'; import { VerificationKeyAsFields } from '../verification_key.js'; /** @@ -17,10 +20,56 @@ export class PrivateCallData { * Public inputs of the private function circuit. */ public publicInputs: PrivateCircuitPublicInputs, + /** * The verification key for the function being invoked. */ public vk: VerificationKeyAsFields, + + /** + * Hints for the validation of the vk + */ + public verificationKeyHints: PrivateVerificationKeyHints, + ) {} + + /** + * Serialize into a field array. Low-level utility. + * @param fields - Object with fields. + * @returns The array. + */ + static getFields(fields: FieldsOf) { + return [fields.publicInputs, fields.vk, fields.verificationKeyHints] as const; + } + + static from(fields: FieldsOf): PrivateCallData { + return new PrivateCallData(...PrivateCallData.getFields(fields)); + } + + /** + * Serialize this as a buffer. + * @returns The buffer. + */ + toBuffer(): Buffer { + return serializeToBuffer(...PrivateCallData.getFields(this)); + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PrivateCallData { + const reader = BufferReader.asReader(buffer); + return new PrivateCallData( + reader.readObject(PrivateCircuitPublicInputs), + reader.readObject(VerificationKeyAsFields), + reader.readObject(PrivateVerificationKeyHints), + ); + } +} + +export class PrivateVerificationKeyHints { + constructor( /** * Artifact hash of the contract class for this private call. */ @@ -46,6 +95,8 @@ export class PrivateCallData { * The hash of the ACIR of the function being invoked. */ public acirHash: Fr, + + public updatedClassIdHints: UpdatedClassIdHints, ) {} /** @@ -53,10 +104,8 @@ export class PrivateCallData { * @param fields - Object with fields. * @returns The array. */ - static getFields(fields: FieldsOf) { + static getFields(fields: FieldsOf) { return [ - fields.publicInputs, - fields.vk, fields.contractClassArtifactHash, fields.contractClassPublicBytecodeCommitment, fields.publicKeys, @@ -64,11 +113,12 @@ export class PrivateCallData { fields.functionLeafMembershipWitness, fields.protocolContractSiblingPath, fields.acirHash, + fields.updatedClassIdHints, ] as const; } - static from(fields: FieldsOf): PrivateCallData { - return new PrivateCallData(...PrivateCallData.getFields(fields)); + static from(fields: FieldsOf): PrivateVerificationKeyHints { + return new PrivateVerificationKeyHints(...PrivateVerificationKeyHints.getFields(fields)); } /** @@ -76,7 +126,7 @@ export class PrivateCallData { * @returns The buffer. */ toBuffer(): Buffer { - return serializeToBuffer(...PrivateCallData.getFields(this)); + return serializeToBuffer(...PrivateVerificationKeyHints.getFields(this)); } /** @@ -84,11 +134,9 @@ export class PrivateCallData { * @param buffer - Buffer or reader to read from. * @returns The deserialized instance. */ - static fromBuffer(buffer: Buffer | BufferReader): PrivateCallData { + static fromBuffer(buffer: Buffer | BufferReader): PrivateVerificationKeyHints { const reader = BufferReader.asReader(buffer); - return new PrivateCallData( - reader.readObject(PrivateCircuitPublicInputs), - reader.readObject(VerificationKeyAsFields), + return new PrivateVerificationKeyHints( reader.readObject(Fr), reader.readObject(Fr), reader.readObject(PublicKeys), @@ -96,6 +144,52 @@ export class PrivateCallData { reader.readObject(MembershipWitness.deserializer(FUNCTION_TREE_HEIGHT)), reader.readArray(PROTOCOL_CONTRACT_TREE_HEIGHT, Fr), reader.readObject(Fr), + reader.readObject(UpdatedClassIdHints), + ); + } +} + +export class UpdatedClassIdHints { + constructor( + public updatedClassIdWitness: MembershipWitness, + public updatedClassIdLeaf: PublicDataTreeLeafPreimage, + public updatedClassIdValueChange: ScheduledValueChange, + public updatedClassIdDelayChange: ScheduledDelayChange, + ) {} + + static getFields(fields: FieldsOf) { + return [ + fields.updatedClassIdWitness, + fields.updatedClassIdLeaf, + fields.updatedClassIdValueChange, + fields.updatedClassIdDelayChange, + ] as const; + } + + static from(fields: FieldsOf): UpdatedClassIdHints { + return new UpdatedClassIdHints(...UpdatedClassIdHints.getFields(fields)); + } + + /** + * Serialize this as a buffer. + * @returns The buffer. + */ + toBuffer(): Buffer { + return serializeToBuffer(...UpdatedClassIdHints.getFields(this)); + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): UpdatedClassIdHints { + const reader = BufferReader.asReader(buffer); + return new UpdatedClassIdHints( + reader.readObject(MembershipWitness.deserializer(PUBLIC_DATA_TREE_HEIGHT)), + reader.readObject(PublicDataTreeLeafPreimage), + reader.readObject(ScheduledValueChange), + reader.readObject(ScheduledDelayChange), ); } } diff --git a/yarn-project/circuits.js/src/structs/shared_mutable/index.ts b/yarn-project/circuits.js/src/structs/shared_mutable/index.ts new file mode 100644 index 00000000000..1a3eebf3713 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/shared_mutable/index.ts @@ -0,0 +1,11 @@ +import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; +import { type Fr } from '@aztec/foundation/fields'; + +import { SHARED_MUTABLE_HASH_SEPARATOR } from '../../constants.gen.js'; + +export * from './scheduled_delay_change.js'; +export * from './scheduled_value_change.js'; + +export async function computeSharedMutableHashSlot(sharedMutableSlot: Fr) { + return await poseidon2HashWithSeparator([sharedMutableSlot], SHARED_MUTABLE_HASH_SEPARATOR); +} diff --git a/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_delay_change.test.ts b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_delay_change.test.ts new file mode 100644 index 00000000000..09e230edf9f --- /dev/null +++ b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_delay_change.test.ts @@ -0,0 +1,17 @@ +import { ScheduledDelayChange } from './scheduled_delay_change.js'; + +describe('ScheduledDelayChange', () => { + it(`serializes to field and back`, () => { + let delayChange = ScheduledDelayChange.empty(); + expect(delayChange).toEqual(ScheduledDelayChange.fromField(delayChange.toField())); + + delayChange = new ScheduledDelayChange(undefined, 1, 2); + expect(delayChange).toEqual(ScheduledDelayChange.fromField(delayChange.toField())); + + delayChange = new ScheduledDelayChange(3, 4, undefined); + expect(delayChange).toEqual(ScheduledDelayChange.fromField(delayChange.toField())); + + delayChange = new ScheduledDelayChange(5, 6, 7); + expect(delayChange).toEqual(ScheduledDelayChange.fromField(delayChange.toField())); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_delay_change.ts b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_delay_change.ts new file mode 100644 index 00000000000..efe4bf58b5a --- /dev/null +++ b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_delay_change.ts @@ -0,0 +1,61 @@ +import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader } from '@aztec/foundation/serialize'; + +import { SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR } from '../../constants.gen.js'; +import { type UInt32 } from '../shared.js'; + +export class ScheduledDelayChange { + constructor(public previous: UInt32 | undefined, public blockOfChange: UInt32, public post: UInt32 | undefined) {} + + static fromField(field: Fr) { + const asBigint = field.toBigInt(); + const post = asBigint & 0xffffffffn; + const pre = (asBigint >> 32n) & 0xffffffffn; + const blockOfChange = (asBigint >> 64n) & 0xffffffffn; + const isPostSome = (asBigint >> 96n) & 1n; + const isPreSome = (asBigint >> 97n) & 1n; + return new this(isPreSome ? Number(pre) : undefined, Number(blockOfChange), isPostSome ? Number(post) : undefined); + } + + toField(): Fr { + // high bits [ pre_is_some: u1 | post_is_some: u1 | block_of_change: u32 | pre_inner: u32 | post_inner: u32 ] low bits + let result = BigInt(this.post || 0); + result |= BigInt(this.previous || 0) << 32n; + result |= BigInt(this.blockOfChange) << 64n; + result |= this.post === undefined ? 0n : 1n << 96n; + result |= this.previous === undefined ? 0n : 1n << 97n; + return new Fr(result); + } + + toBuffer(): Buffer { + return this.toField().toBuffer(); + } + + static fromBuffer(buffer: Buffer | BufferReader): ScheduledDelayChange { + const reader = BufferReader.asReader(buffer); + return ScheduledDelayChange.fromField(reader.readObject(Fr)); + } + + static empty() { + return new this(undefined, 0, undefined); + } + + isEmpty(): boolean { + return this.toField().isEmpty(); + } + + static async computeSlot(sharedMutableSlot: Fr) { + return await poseidon2HashWithSeparator([sharedMutableSlot], SHARED_MUTABLE_DELAY_CHANGE_SEPARATOR); + } + + static async readFromTree(sharedMutableSlot: Fr, reader: (storageSlot: Fr) => Promise) { + const delaySlot = await this.computeSlot(sharedMutableSlot); + return ScheduledDelayChange.fromField(await reader(delaySlot)); + } + + async writeToTree(sharedMutableSlot: Fr, writer: (storageSlot: Fr, value: Fr) => Promise) { + const delaySlot = await ScheduledDelayChange.computeSlot(sharedMutableSlot); + await writer(delaySlot, this.toField()); + } +} diff --git a/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_value_change.test.ts b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_value_change.test.ts new file mode 100644 index 00000000000..eac95dbad28 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_value_change.test.ts @@ -0,0 +1,14 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { ScheduledValueChange } from './scheduled_value_change.js'; + +describe('ScheduledValueChange', () => { + it(`serializes to fields and back`, () => { + let valueChange = ScheduledValueChange.empty(); + expect(valueChange).toEqual(ScheduledValueChange.fromFields(valueChange.toFields())); + + valueChange = new ScheduledValueChange(new Fr(1), new Fr(2), 27); + + expect(valueChange).toEqual(ScheduledValueChange.fromFields(valueChange.toFields())); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_value_change.ts b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_value_change.ts new file mode 100644 index 00000000000..bd188bd719b --- /dev/null +++ b/yarn-project/circuits.js/src/structs/shared_mutable/scheduled_value_change.ts @@ -0,0 +1,64 @@ +import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR } from '../../constants.gen.js'; + +export class ScheduledValueChange { + constructor(public previous: Fr, public post: Fr, public blockOfChange: number) {} + + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return new this(reader.readField(), reader.readField(), reader.readField().toNumber()); + } + + toFields(): Tuple { + return [this.previous, this.post, new Fr(this.blockOfChange)]; + } + + toBuffer(): Buffer { + return serializeToBuffer(this.toFields()); + } + + static fromBuffer(buffer: Buffer | BufferReader): ScheduledValueChange { + const reader = BufferReader.asReader(buffer); + return ScheduledValueChange.fromFields(reader.readArray(3, Fr)); + } + + static empty() { + return new this(Fr.ZERO, Fr.ZERO, 0); + } + + isEmpty(): boolean { + return this.previous.isZero() && this.blockOfChange === 0 && this.post.isZero(); + } + + static async computeSlot(sharedMutableSlot: Fr) { + return await poseidon2HashWithSeparator([sharedMutableSlot], SHARED_MUTABLE_VALUE_CHANGE_SEPARATOR); + } + + static async readFromTree(sharedMutableSlot: Fr, reader: (storageSlot: Fr) => Promise) { + const baseValueSlot = await this.computeSlot(sharedMutableSlot); + const fields = []; + for (let i = 0; i < 3; i++) { + fields.push(await reader(baseValueSlot.add(new Fr(i)))); + } + return ScheduledValueChange.fromFields(fields); + } + + async writeToTree(sharedMutableSlot: Fr, writer: (storageSlot: Fr, value: Fr) => Promise) { + const baseValueSlot = await ScheduledValueChange.computeSlot(sharedMutableSlot); + const fields = this.toFields(); + for (let i = 0; i < 3; i++) { + await writer(baseValueSlot.add(new Fr(i)), fields[i]); + } + } + + getCurrentAt(blockNumber: number) { + if (blockNumber < this.blockOfChange) { + return this.previous; + } else { + return this.post; + } + } +} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 1d0cc54296e..0dca4d78c27 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -1333,6 +1333,7 @@ export async function makeContractInstanceFromClassId( deployer?: AztecAddress; initializationHash?: Fr; publicKeys?: PublicKeys; + currentClassId?: Fr; }, ): Promise { const salt = new Fr(seed); @@ -1353,7 +1354,8 @@ export async function makeContractInstanceFromClassId( version: 1, salt, deployer, - contractClassId: classId, + currentContractClassId: overrides?.currentClassId ?? classId, + originalContractClassId: classId, initializationHash, publicKeys, }).withAddress(address); @@ -1368,10 +1370,13 @@ export async function makeAvmBytecodeHints(seed = 0): Promise new Fr(i), seed + 0x4000), ); const publicBytecodeCommitment = await computePublicBytecodeCommitment(packedBytecode); @@ -1400,6 +1405,14 @@ export function makeAvmNullifierReadTreeHints(seed = 0): AvmNullifierReadTreeHin ); } +export function makeAvmPublicDataReadTreeHints(seed = 0): AvmPublicDataReadTreeHint { + return new AvmPublicDataReadTreeHint( + new PublicDataTreeLeafPreimage(new Fr(seed), new Fr(seed + 1), new Fr(seed + 2), BigInt(seed + 3)), + new Fr(seed + 1), + makeArray(10, i => new Fr(i), seed + 0x1000), + ); +} + export function makeAvmNullifierInsertionTreeHints(seed = 0): AvmNullifierWriteTreeHint { return new AvmNullifierWriteTreeHint( makeAvmNullifierReadTreeHints(seed), @@ -1436,13 +1449,16 @@ export function makeAvmContractInstanceHint(seed = 0): AvmContractInstanceHint { new AztecAddress(new Fr(seed + 0x3)), new Fr(seed + 0x4), new Fr(seed + 0x5), + new Fr(seed + 0x6), new PublicKeys( - new Point(new Fr(seed + 0x6), new Fr(seed + 0x7), false), - new Point(new Fr(seed + 0x8), new Fr(seed + 0x9), false), - new Point(new Fr(seed + 0x10), new Fr(seed + 0x11), false), - new Point(new Fr(seed + 0x12), new Fr(seed + 0x13), false), + new Point(new Fr(seed + 0x7), new Fr(seed + 0x8), false), + new Point(new Fr(seed + 0x9), new Fr(seed + 0x10), false), + new Point(new Fr(seed + 0x11), new Fr(seed + 0x12), false), + new Point(new Fr(seed + 0x13), new Fr(seed + 0x14), false), ), makeAvmNullifierReadTreeHints(seed + 0x1000), + makeAvmPublicDataReadTreeHints(seed + 0x2000), + makeArray(4, i => new Fr(i), seed + 0x3000), ); } diff --git a/yarn-project/cli/src/cmds/pxe/add_contract.ts b/yarn-project/cli/src/cmds/pxe/add_contract.ts index d43ba9c586d..b62ce6beb23 100644 --- a/yarn-project/cli/src/cmds/pxe/add_contract.ts +++ b/yarn-project/cli/src/cmds/pxe/add_contract.ts @@ -18,11 +18,13 @@ export async function addContract( log: LogFn, ) { const artifact = await getContractArtifact(contractArtifactPath, log); + const contractClass = await getContractClassFromArtifact(artifact); const instance: ContractInstanceWithAddress = { version: 1, salt, initializationHash, - contractClassId: (await getContractClassFromArtifact(artifact)).id, + currentContractClassId: contractClass.id, + originalContractClassId: contractClass.id, publicKeys: publicKeys ?? PublicKeys.default(), address, deployer: deployer ?? AztecAddress.ZERO, @@ -35,5 +37,5 @@ export async function addContract( const client = await createCompatibleClient(rpcUrl, debugLogger); await client.registerContract({ artifact, instance }); - log(`\nContract added to PXE at ${address.toString()} with class ${instance.contractClassId.toString()}\n`); + log(`\nContract added to PXE at ${address.toString()} with class ${instance.currentContractClassId.toString()}\n`); } diff --git a/yarn-project/cli/src/cmds/pxe/get_contract_data.ts b/yarn-project/cli/src/cmds/pxe/get_contract_data.ts index ff42abda7a5..aa3348ba3f8 100644 --- a/yarn-project/cli/src/cmds/pxe/get_contract_data.ts +++ b/yarn-project/cli/src/cmds/pxe/get_contract_data.ts @@ -16,7 +16,9 @@ export async function getContractData( isContractPubliclyDeployed: isPubliclyDeployed, } = await client.getContractMetadata(contractAddress); const contractClass = - includeBytecode && instance && (await client.getContractClassMetadata(instance?.contractClassId)).contractClass; + includeBytecode && + instance && + (await client.getContractClassMetadata(instance?.currentContractClassId)).contractClass; const isPrivatelyDeployed = !!instance; const initStr = isInitialized ? 'initialized' : 'not initialized'; diff --git a/yarn-project/cli/src/utils/inspect.ts b/yarn-project/cli/src/utils/inspect.ts index 2fcea0f3ca9..f45de2a89ff 100644 --- a/yarn-project/cli/src/utils/inspect.ts +++ b/yarn-project/cli/src/utils/inspect.ts @@ -184,7 +184,7 @@ async function getKnownArtifacts(pxe: PXE): Promise { const knownContracts = ( await Promise.all(knownContractAddresses.map(contractAddress => pxe.getContractMetadata(contractAddress))) ).map(contractMetadata => contractMetadata.contractInstance); - const classIds = [...new Set(knownContracts.map(contract => contract?.contractClassId))]; + const classIds = [...new Set(knownContracts.map(contract => contract?.currentContractClassId))]; const knownArtifacts = ( await Promise.all(classIds.map(classId => (classId ? pxe.getContractClassMetadata(classId) : undefined))) ).map(contractClassMetadata => @@ -196,7 +196,7 @@ async function getKnownArtifacts(pxe: PXE): Promise { for (const instance of knownContracts) { if (instance) { const artifact = knownArtifacts.find(a => - a?.classId?.equals(instance.contractClassId), + a?.classId?.equals(instance.currentContractClassId), ) as ContractArtifactWithClassId; if (artifact) { map[instance.address.toString()] = artifact; 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 fc40776e275..13b573cb861 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 @@ -118,7 +118,7 @@ describe('e2e_avm_simulator', () => { .test_get_contract_instance_matches( avmContract.address, avmContract.instance.deployer, - avmContract.instance.contractClassId, + avmContract.instance.currentContractClassId, avmContract.instance.initializationHash, ) .send() diff --git a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts new file mode 100644 index 00000000000..b7ffd1a11df --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts @@ -0,0 +1,52 @@ +import { type Fr, type Logger, type Wallet, getContractClassFromArtifact } from '@aztec/aztec.js'; +import { registerContractClass } from '@aztec/aztec.js/deployment'; +import { UpdatableContract } from '@aztec/noir-contracts.js/Updatable'; +import { UpdatedContract, UpdatedContractArtifact } from '@aztec/noir-contracts.js/Updated'; + +import { setup } from './fixtures/utils.js'; + +describe('e2e_contract_updates', () => { + let wallet: Wallet; + let teardown: () => Promise; + let contract: UpdatableContract; + let updatedContractClassId: Fr; + let logger: Logger; + + beforeAll(async () => { + ({ teardown, wallet, logger } = await setup()); + contract = await UpdatableContract.deploy(wallet, 1n).send().deployed(); + const registerMethod = await registerContractClass(wallet, UpdatedContractArtifact); + await registerMethod.send().wait(); + + updatedContractClassId = (await getContractClassFromArtifact(UpdatedContractArtifact)).id; + }); + + afterAll(() => teardown()); + + it('should update the contract', async () => { + expect(await contract.methods.get_private_value().simulate()).toEqual(1n); + expect(await contract.methods.get_public_value().simulate()).toEqual(1n); + await contract.methods.update_to(updatedContractClassId).send().wait(); + // Mine some blocks + logger.info('Waiting for update to apply'); + for (let i = 0; i < 12; i++) { + try { + await contract.methods.set_public_value(1n).send().wait(); + } catch (e) { + // Fails when updated since the method doesn't exist anymore + break; + } + } + logger.info('Done waiting'); + + const updatedContract = await UpdatedContract.at(contract.address, wallet); + // Call a private method that wasn't available in the previous contract + await updatedContract.methods.set_private_value().send().wait(); + // Read state that was changed by the previous tx + expect(await updatedContract.methods.get_private_value().simulate()).toEqual(27n); + + // Call a public method with a new implementation + await updatedContract.methods.set_public_value().send().wait(); + expect(await updatedContract.methods.get_public_value().simulate()).toEqual(27n); + }); +}); 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 3798d571b7e..124b8c47d47 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 @@ -155,7 +155,7 @@ describe('e2e_deploy_contract contract class registration', () => { constructorArtifact: opts.constructorName, deployer: opts.deployer, }); - const { address, contractClassId } = instance; + const { address, currentContractClassId: contractClassId } = instance; logger.info(`Deploying contract instance at ${address.toString()} class id ${contractClassId.toString()}`); await deployFn(instance); @@ -198,7 +198,7 @@ describe('e2e_deploy_contract contract class registration', () => { const deployed = await aztecNode.getContract(instance.address); expect(deployed).toBeDefined(); expect(deployed!.address).toEqual(instance.address); - expect(deployed!.contractClassId).toEqual(contractClass.id); + expect(deployed!.currentContractClassId).toEqual(contractClass.id); expect(deployed!.initializationHash).toEqual(instance.initializationHash); expect(deployed!.publicKeys).toEqual(instance.publicKeys); expect(deployed!.salt).toEqual(instance.salt); 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 07b9af169ad..4743a41be9c 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 @@ -51,7 +51,7 @@ describe('e2e_deploy_contract deploy method', () => { await contract.methods.increment_public_value(owner, 84).send().wait(); expect(await contract.methods.get_public_value(owner).simulate()).toEqual(84n); expect( - (await pxe.getContractClassMetadata(contract.instance.contractClassId)).isContractClassPubliclyRegistered, + (await pxe.getContractClassMetadata(contract.instance.currentContractClassId)).isContractClassPubliclyRegistered, ).toBeTrue(); }); 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 a388abe9ef2..0616a7ddb7a 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 @@ -116,7 +116,7 @@ describe('e2e_deploy_contract legacy', () => { const { isContractClassPubliclyRegistered } = await pxe.getContractClassMetadata( ( await badDeploy.getInstance() - ).contractClassId, + ).currentContractClassId, ); // But the bad tx did not deploy expect(isContractClassPubliclyRegistered).toBeFalse(); 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 4682ac112aa..48f68e3d846 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -575,7 +575,7 @@ describe('e2e_synching', () => { const contractClassIds = await archiver.getContractClassIds(); for (const c of contracts) { - expect(contractClassIds.includes(c.instance.contractClassId)).toBeTrue; + expect(contractClassIds.includes(c.instance.currentContractClassId)).toBeTrue; expect(await archiver.getContract(c.address)).not.toBeUndefined; } @@ -593,15 +593,15 @@ describe('e2e_synching', () => { const contractClassIdsAfter = await archiver.getContractClassIds(); - expect(contractClassIdsAfter.includes(contracts[0].instance.contractClassId)).toBeTrue; - expect(contractClassIdsAfter.includes(contracts[1].instance.contractClassId)).toBeFalse; + expect(contractClassIdsAfter.includes(contracts[0].instance.currentContractClassId)).toBeTrue; + expect(contractClassIdsAfter.includes(contracts[1].instance.currentContractClassId)).toBeFalse; expect(await archiver.getContract(contracts[0].address)).not.toBeUndefined; expect(await archiver.getContract(contracts[1].address)).toBeUndefined; expect(await archiver.getContract(contracts[2].address)).toBeUndefined; // Only the hardcoded schnorr is pruned since the contract class also existed before prune. expect(contractClassIdsAfter).toEqual( - contractClassIds.filter(c => !c.equals(contracts[1].instance.contractClassId)), + contractClassIds.filter(c => !c.equals(contracts[1].instance.currentContractClassId)), ); expect(await archiver.getTxEffect(txHash)).toBeUndefined; diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index 9f5427bad37..b90142a429a 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -560,7 +560,7 @@ export const addAccounts = if (index === 0) { // for the first account, check if the contract class is already registered, otherwise we should register now if ( - !(await pxe.getContractClassMetadata(account.getInstance().contractClassId)) + !(await pxe.getContractClassMetadata(account.getInstance().currentContractClassId)) .isContractClassPubliclyRegistered ) { skipClassRegistration = false; diff --git a/yarn-project/ivc-integration/src/avm_integration.test.ts b/yarn-project/ivc-integration/src/avm_integration.test.ts index 31824adaf24..95ecaf7ab4a 100644 --- a/yarn-project/ivc-integration/src/avm_integration.test.ts +++ b/yarn-project/ivc-integration/src/avm_integration.test.ts @@ -101,7 +101,7 @@ describe('AVM Integration', () => { ...argsU8, /*getInstanceForAddress=*/ expectContractInstance.address.toField(), /*expectedDeployer=*/ expectContractInstance.deployer.toField(), - /*expectedClassId=*/ expectContractInstance.contractClassId.toField(), + /*expectedClassId=*/ expectContractInstance.currentContractClassId.toField(), /*expectedInitializationHash=*/ expectContractInstance.initializationHash.toField(), ].map(x => new Fr(x)); const simRes = await simTester.simulateTx( diff --git a/yarn-project/kv-store/src/interfaces/common.ts b/yarn-project/kv-store/src/interfaces/common.ts index 8edf29a3e01..f7123038f83 100644 --- a/yarn-project/kv-store/src/interfaces/common.ts +++ b/yarn-project/kv-store/src/interfaces/common.ts @@ -1,7 +1,7 @@ /** * The key type for use with the kv-store */ -export type Key = string | number; +export type Key = string | number | Array; /** * A range of keys to iterate over. diff --git a/yarn-project/noir-protocol-circuits-types/src/conversion/client.ts b/yarn-project/noir-protocol-circuits-types/src/conversion/client.ts index 0a68d04ca01..2b1b13fe4e6 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/client.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/client.ts @@ -39,6 +39,7 @@ import { PrivateLogData, PrivateToPublicAccumulatedData, PrivateValidationRequests, + type PrivateVerificationKeyHints, type PublicKeys, ReadRequest, type ReadRequestStatus, @@ -85,6 +86,7 @@ import type { PrivateToPublicKernelCircuitPublicInputs as PrivateToPublicKernelCircuitPublicInputsNoir, PrivateToRollupKernelCircuitPublicInputs as PrivateToRollupKernelCircuitPublicInputsNoir, PrivateValidationRequests as PrivateValidationRequestsNoir, + PrivateVerificationKeyHints as PrivateVerificationKeyHintsNoir, PublicKeys as PublicKeysNoir, ReadRequest as ReadRequestNoir, ReadRequestStatus as ReadRequestStatusNoir, @@ -126,6 +128,7 @@ import { mapPrivateToRollupAccumulatedDataFromNoir, mapPublicCallRequestFromNoir, mapPublicCallRequestToNoir, + mapPublicDataTreePreimageToNoir, mapScopedL2ToL1MessageFromNoir, mapScopedL2ToL1MessageToNoir, mapTupleFromNoir, @@ -603,6 +606,37 @@ export function mapFunctionDataFromNoir(functionData: FunctionDataNoir): Functio return new FunctionData(mapFunctionSelectorFromNoir(functionData.selector), functionData.is_private); } +export function mapPrivateVerificationKeyHintsToNoir( + privateVerificationKeyHints: PrivateVerificationKeyHints, +): PrivateVerificationKeyHintsNoir { + return { + function_leaf_membership_witness: mapMembershipWitnessToNoir( + privateVerificationKeyHints.functionLeafMembershipWitness, + ), + contract_class_artifact_hash: mapFieldToNoir(privateVerificationKeyHints.contractClassArtifactHash), + contract_class_public_bytecode_commitment: mapFieldToNoir( + privateVerificationKeyHints.contractClassPublicBytecodeCommitment, + ), + public_keys: mapPublicKeysToNoir(privateVerificationKeyHints.publicKeys), + salted_initialization_hash: mapWrappedFieldToNoir(privateVerificationKeyHints.saltedInitializationHash), + protocol_contract_sibling_path: mapTuple(privateVerificationKeyHints.protocolContractSiblingPath, mapFieldToNoir), + acir_hash: mapFieldToNoir(privateVerificationKeyHints.acirHash), + updated_class_id_witness: mapMembershipWitnessToNoir( + privateVerificationKeyHints.updatedClassIdHints.updatedClassIdWitness, + ), + updated_class_id_leaf: mapPublicDataTreePreimageToNoir( + privateVerificationKeyHints.updatedClassIdHints.updatedClassIdLeaf, + ), + updated_class_id_value_change: mapTuple( + privateVerificationKeyHints.updatedClassIdHints.updatedClassIdValueChange.toFields(), + mapFieldToNoir, + ), + updated_class_id_delay_change: [ + mapFieldToNoir(privateVerificationKeyHints.updatedClassIdHints.updatedClassIdDelayChange.toField()), + ], + }; +} + /** * Maps a private call data to a noir private call data. * @param privateCallData - The private call data. @@ -611,13 +645,7 @@ export function mapFunctionDataFromNoir(functionData: FunctionDataNoir): Functio export function mapPrivateCallDataToNoir(privateCallData: PrivateCallData): PrivateCallDataWithoutPublicInputsNoir { return { vk: mapVerificationKeyToNoir(privateCallData.vk, CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS), - function_leaf_membership_witness: mapMembershipWitnessToNoir(privateCallData.functionLeafMembershipWitness), - contract_class_artifact_hash: mapFieldToNoir(privateCallData.contractClassArtifactHash), - contract_class_public_bytecode_commitment: mapFieldToNoir(privateCallData.contractClassPublicBytecodeCommitment), - public_keys: mapPublicKeysToNoir(privateCallData.publicKeys), - salted_initialization_hash: mapWrappedFieldToNoir(privateCallData.saltedInitializationHash), - protocol_contract_sibling_path: mapTuple(privateCallData.protocolContractSiblingPath, mapFieldToNoir), - acir_hash: mapFieldToNoir(privateCallData.acirHash), + verification_key_hints: mapPrivateVerificationKeyHintsToNoir(privateCallData.verificationKeyHints), }; } diff --git a/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts b/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts index 2ce0f96fcb3..f98e2caaa70 100644 --- a/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts +++ b/yarn-project/noir-protocol-circuits-types/src/noir_test_gen.test.ts @@ -57,7 +57,14 @@ describe('Data generation for noir tests', () => { contractClass, ); const deployer = AztecAddress.ZERO; - const instance: ContractInstance = { ...contract, version: 1, initializationHash, contractClassId, deployer }; + const instance: ContractInstance = { + ...contract, + version: 1, + initializationHash, + currentContractClassId: contractClassId, + originalContractClassId: contractClassId, + deployer, + }; const address = await computeContractAddressFromInstance(instance); const saltedInitializationHash = await computeSaltedInitializationHash(instance); const partialAddress = await computePartialAddress(instance); diff --git a/yarn-project/noir-protocol-circuits-types/src/utils/client/foreign_call_handler.ts b/yarn-project/noir-protocol-circuits-types/src/utils/client/foreign_call_handler.ts index e01c95a83f9..fde85d54776 100644 --- a/yarn-project/noir-protocol-circuits-types/src/utils/client/foreign_call_handler.ts +++ b/yarn-project/noir-protocol-circuits-types/src/utils/client/foreign_call_handler.ts @@ -18,6 +18,8 @@ export function foreignCallHandler(name: string, args: ForeignCallInput[]): Prom const msg: string = msgRaw.map(acvmField => String.fromCharCode(fromACVMField(acvmField).toNumber())).join(''); const fieldsFr: Fr[] = fields.map((field: string) => fromACVMField(field)); log.verbose('debug_log ' + applyStringFormatting(msg, fieldsFr)); + } else if (name === 'noOp') { + // Workaround for compiler issues where data is deleted because it's "unused" } else { throw Error(`unexpected oracle during execution: ${name}`); } diff --git a/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts b/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts index 00ce35f286f..1db57af7952 100644 --- a/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts +++ b/yarn-project/noir-protocol-circuits-types/src/utils/server/foreign_call_handler.ts @@ -59,6 +59,8 @@ export async function foreignCallHandler(name: string, args: ForeignCallInput[]) } }); return Promise.resolve([blobPublicInputs.toFields().map(toACVMField)]); + } else if (name === 'noOp') { + // Workaround for compiler issues where data is deleted because it's "unused" } else { throw Error(`unexpected oracle during execution: ${name}`); } diff --git a/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_deployed_event.ts b/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_deployed_event.ts index 500f87f82fd..163cf5b3778 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_deployed_event.ts +++ b/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_deployed_event.ts @@ -51,7 +51,8 @@ export class ContractInstanceDeployedEvent { return { address: this.address, version: this.version, - contractClassId: this.contractClassId, + currentContractClassId: this.contractClassId, + originalContractClassId: this.contractClassId, initializationHash: this.initializationHash, publicKeys: this.publicKeys, salt: this.salt, diff --git a/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_updated_event.ts b/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_updated_event.ts new file mode 100644 index 00000000000..4fb32f3fbe4 --- /dev/null +++ b/yarn-project/protocol-contracts/src/instance-deployer/contract_instance_updated_event.ts @@ -0,0 +1,43 @@ +import { type ContractInstanceUpdateWithAddress, type PublicLog } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader } from '@aztec/foundation/serialize'; + +import { DEPLOYER_CONTRACT_INSTANCE_UPDATED_TAG, ProtocolContractAddress } from '../protocol_contract_data.js'; + +/** Event emitted from the ContractInstanceDeployer. */ +export class ContractInstanceUpdatedEvent { + constructor( + public readonly address: AztecAddress, + public readonly prevContractClassId: Fr, + public readonly newContractClassId: Fr, + public readonly blockOfChange: number, + ) {} + + static isContractInstanceUpdatedEvent(log: PublicLog) { + return ( + log.contractAddress.equals(ProtocolContractAddress.ContractInstanceDeployer) && + log.log[0].equals(DEPLOYER_CONTRACT_INSTANCE_UPDATED_TAG) + ); + } + + static fromLog(log: PublicLog) { + const bufferWithoutAddressAndTag = log.toBuffer().subarray(64); + const reader = new BufferReader(bufferWithoutAddressAndTag); + const address = reader.readObject(AztecAddress); + const prevContractClassId = reader.readObject(Fr); + const newContractClassId = reader.readObject(Fr); + const blockOfChange = reader.readObject(Fr).toNumber(); + + return new ContractInstanceUpdatedEvent(address, prevContractClassId, newContractClassId, blockOfChange); + } + + toContractInstanceUpdate(): ContractInstanceUpdateWithAddress { + return { + address: this.address, + prevContractClassId: this.prevContractClassId, + newContractClassId: this.newContractClassId, + blockOfChange: this.blockOfChange, + }; + } +} diff --git a/yarn-project/protocol-contracts/src/instance-deployer/index.ts b/yarn-project/protocol-contracts/src/instance-deployer/index.ts index dff020eb191..28d61b72f7c 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/index.ts +++ b/yarn-project/protocol-contracts/src/instance-deployer/index.ts @@ -6,6 +6,7 @@ import { makeProtocolContract } from '../make_protocol_contract.js'; import { type ProtocolContract } from '../protocol_contract.js'; export * from './contract_instance_deployed_event.js'; +export * from './contract_instance_updated_event.js'; export const ContractInstanceDeployerArtifact = loadContractArtifact( ContractInstanceDeployerJson as NoirCompiledContract, diff --git a/yarn-project/protocol-contracts/src/scripts/generate_data.ts b/yarn-project/protocol-contracts/src/scripts/generate_data.ts index fa5b0e6c4b1..de306d1bbaa 100644 --- a/yarn-project/protocol-contracts/src/scripts/generate_data.ts +++ b/yarn-project/protocol-contracts/src/scripts/generate_data.ts @@ -3,6 +3,7 @@ import { CANONICAL_AUTH_REGISTRY_ADDRESS, DEPLOYER_CONTRACT_ADDRESS, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, + DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE, FEE_JUICE_ADDRESS, Fr, MULTI_CALL_ENTRYPOINT_ADDRESS, @@ -138,6 +139,7 @@ async function generateLogTags() { DEPLOYER_CONTRACT_ADDRESS, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE, ])}'); + export const DEPLOYER_CONTRACT_INSTANCE_UPDATED_TAG = new Fr(${DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE}n); `; } diff --git a/yarn-project/pxe/src/contract_data_oracle/index.ts b/yarn-project/pxe/src/contract_data_oracle/index.ts index 86963078092..aef5653fa44 100644 --- a/yarn-project/pxe/src/contract_data_oracle/index.ts +++ b/yarn-project/pxe/src/contract_data_oracle/index.ts @@ -23,21 +23,16 @@ import { PrivateFunctionsTree } from './private_functions_tree.js'; export class ContractDataOracle { /** Map from contract class id to private function tree. */ private contractClasses: Map = new Map(); - /** Map from address to contract instance. */ - private contractInstances: Map = new Map(); constructor(private db: ContractArtifactDatabase & ContractInstanceDatabase) {} /** Returns a contract instance for a given address. Throws if not found. */ public async getContractInstance(contractAddress: AztecAddress): Promise { - if (!this.contractInstances.has(contractAddress.toString())) { - const instance = await this.db.getContractInstance(contractAddress); - if (!instance) { - throw new ContractNotFoundError(contractAddress.toString()); - } - this.contractInstances.set(contractAddress.toString(), instance); + const instance = await this.db.getContractInstance(contractAddress); + if (!instance) { + throw new ContractNotFoundError(contractAddress.toString()); } - return this.contractInstances.get(contractAddress.toString())!; + return instance; } /** Returns a contract class for a given class id. Throws if not found. */ @@ -117,16 +112,16 @@ export class ContractDataOracle { } /** - * Retrieve the function membership witness for the given contract address and function selector. + * Retrieve the function membership witness for the given contract class and function selector. * The function membership witness represents a proof that the function belongs to the specified contract. * Throws an error if the contract address or function selector is unknown. * - * @param contractAddress - The contract address. + * @param contractClassId - The id of the class. * @param selector - The function selector. * @returns A promise that resolves with the MembershipWitness instance for the specified contract's function. */ - public async getFunctionMembershipWitness(contractAddress: AztecAddress, selector: FunctionSelector) { - const tree = await this.getTreeForAddress(contractAddress); + public async getFunctionMembershipWitness(contractClassId: Fr, selector: FunctionSelector) { + const tree = await this.getTreeForClassId(contractClassId); return tree.getFunctionMembershipWitness(selector); } @@ -176,6 +171,6 @@ export class ContractDataOracle { */ private async getTreeForAddress(contractAddress: AztecAddress): Promise { const instance = await this.getContractInstance(contractAddress); - return this.getTreeForClassId(instance.contractClassId); + return this.getTreeForClassId(instance.currentContractClassId); } } diff --git a/yarn-project/pxe/src/database/kv_pxe_database.ts b/yarn-project/pxe/src/database/kv_pxe_database.ts index 05c7faa4b0d..c662ceb7197 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.ts @@ -127,7 +127,7 @@ export class KVPxeDatabase implements PxeDatabase { address: AztecAddress, ): Promise<(ContractInstanceWithAddress & ContractArtifact) | undefined> { const instance = await this.getContractInstance(address); - const artifact = instance && (await this.getContractArtifact(instance?.contractClassId)); + const artifact = instance && (await this.getContractArtifact(instance?.currentContractClassId)); if (!instance || !artifact) { return undefined; } diff --git a/yarn-project/pxe/src/kernel_oracle/index.ts b/yarn-project/pxe/src/kernel_oracle/index.ts index 6e8e67de8f1..89ce7303e89 100644 --- a/yarn-project/pxe/src/kernel_oracle/index.ts +++ b/yarn-project/pxe/src/kernel_oracle/index.ts @@ -1,21 +1,29 @@ import { type AztecNode, type L2BlockNumber } from '@aztec/circuit-types'; import { type AztecAddress, - type Fr, + Fr, type FunctionSelector, type GrumpkinScalar, MembershipWitness, type NOTE_HASH_TREE_HEIGHT, + PUBLIC_DATA_TREE_HEIGHT, type Point, + ScheduledDelayChange, + ScheduledValueChange, + UPDATED_CLASS_IDS_SLOT, + UpdatedClassIdHints, VK_TREE_HEIGHT, type VerificationKeyAsFields, computeContractClassIdPreimage, computeSaltedInitializationHash, + computeSharedMutableHashSlot, } from '@aztec/circuits.js'; +import { computePublicDataTreeLeafSlot, deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; import { createLogger } from '@aztec/foundation/log'; import { type Tuple } from '@aztec/foundation/serialize'; import { type KeyStore } from '@aztec/key-store'; import { getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-types/vks'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { type ContractDataOracle } from '../contract_data_oracle/index.js'; import { type ProvingDataOracle } from './../kernel_prover/proving_data_oracle.js'; @@ -47,8 +55,8 @@ export class KernelOracle implements ProvingDataOracle { return computeContractClassIdPreimage(contractClass); } - public async getFunctionMembershipWitness(contractAddress: AztecAddress, selector: FunctionSelector) { - return await this.contractDataOracle.getFunctionMembershipWitness(contractAddress, selector); + public async getFunctionMembershipWitness(contractClassId: Fr, selector: FunctionSelector) { + return await this.contractDataOracle.getFunctionMembershipWitness(contractClassId, selector); } public async getVkMembershipWitness(vk: VerificationKeyAsFields) { @@ -81,4 +89,37 @@ export class KernelOracle implements ProvingDataOracle { public getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise { return this.contractDataOracle.getDebugFunctionName(contractAddress, selector); } + + public async getUpdatedClassIdHints(contractAddress: AztecAddress): Promise { + const sharedMutableSlot = await deriveStorageSlotInMap(new Fr(UPDATED_CLASS_IDS_SLOT), contractAddress); + + const hashSlot = await computeSharedMutableHashSlot(sharedMutableSlot); + + const hashLeafSlot = await computePublicDataTreeLeafSlot( + ProtocolContractAddress.ContractInstanceDeployer, + hashSlot, + ); + const updatedClassIdWitness = await this.node.getPublicDataTreeWitness(this.blockNumber, hashLeafSlot); + + if (!updatedClassIdWitness) { + throw new Error(`No public data tree witness found for ${hashLeafSlot}`); + } + + const readStorage = (storageSlot: Fr) => + this.node.getPublicStorageAt(ProtocolContractAddress.ContractInstanceDeployer, storageSlot, this.blockNumber); + + const valueChange = await ScheduledValueChange.readFromTree(sharedMutableSlot, readStorage); + const delayChange = await ScheduledDelayChange.readFromTree(sharedMutableSlot, readStorage); + + return new UpdatedClassIdHints( + new MembershipWitness( + PUBLIC_DATA_TREE_HEIGHT, + updatedClassIdWitness.index, + updatedClassIdWitness.siblingPath.toTuple(), + ), + updatedClassIdWitness.leafPreimage, + valueChange, + delayChange, + ); + } } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index 2b53c5bc8b7..c7b9ea9b117 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -141,7 +141,8 @@ describe('Kernel Prover', () => { oracle.getVkMembershipWitness.mockResolvedValue(MembershipWitness.random(VK_TREE_HEIGHT)); oracle.getContractAddressPreimage.mockResolvedValue({ - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), publicKeys: await PublicKeys.random(), saltedInitializationHash: Fr.random(), }); diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index b4aad8fc7ad..7cb28c24e63 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -22,6 +22,7 @@ import { PrivateKernelTailCircuitPrivateInputs, type PrivateKernelTailCircuitPublicInputs, type PrivateLog, + PrivateVerificationKeyHints, type ScopedPrivateLogData, type TxRequest, VK_TREE_HEIGHT, @@ -342,15 +343,15 @@ export class KernelProver { const vkAsFields = await vkAsFieldsMegaHonk(vkAsBuffer); const vk = new VerificationKeyAsFields(vkAsFields, await hashVK(vkAsFields)); + const { currentContractClassId, publicKeys, saltedInitializationHash } = + await this.oracle.getContractAddressPreimage(contractAddress); const functionLeafMembershipWitness = await this.oracle.getFunctionMembershipWitness( - contractAddress, + currentContractClassId, functionSelector, ); - const { contractClassId, publicKeys, saltedInitializationHash } = await this.oracle.getContractAddressPreimage( - contractAddress, - ); + const { artifactHash: contractClassArtifactHash, publicBytecodeCommitment: contractClassPublicBytecodeCommitment } = - await this.oracle.getContractClassIdPreimage(contractClassId); + await this.oracle.getContractClassIdPreimage(currentContractClassId); // TODO(#262): Use real acir hash // const acirHash = keccak256(Buffer.from(bytecode, 'hex')); @@ -360,16 +361,20 @@ export class KernelProver { ? await getProtocolContractSiblingPath(contractAddress) : makeTuple(PROTOCOL_CONTRACT_TREE_HEIGHT, Fr.zero); + const updatedClassIdHints = await this.oracle.getUpdatedClassIdHints(contractAddress); return PrivateCallData.from({ publicInputs, vk, - publicKeys, - contractClassArtifactHash, - contractClassPublicBytecodeCommitment, - saltedInitializationHash, - functionLeafMembershipWitness, - protocolContractSiblingPath, - acirHash, + verificationKeyHints: PrivateVerificationKeyHints.from({ + publicKeys, + contractClassArtifactHash, + contractClassPublicBytecodeCommitment, + saltedInitializationHash, + functionLeafMembershipWitness, + protocolContractSiblingPath, + acirHash, + updatedClassIdHints, + }), }); } diff --git a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts index c3127fbc210..21f5013df12 100644 --- a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts +++ b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts @@ -8,6 +8,7 @@ import { type NOTE_HASH_TREE_HEIGHT, type Point, type PublicKeys, + type UpdatedClassIdHints, type VK_TREE_HEIGHT, type VerificationKeyAsFields, } from '@aztec/circuits.js'; @@ -19,9 +20,12 @@ import { type AztecAddress } from '@aztec/foundation/aztec-address'; */ export interface ProvingDataOracle { /** Retrieves the preimage of a contract address from the registered contract instances db. */ - getContractAddressPreimage( - address: AztecAddress, - ): Promise<{ saltedInitializationHash: Fr; publicKeys: PublicKeys; contractClassId: Fr }>; + getContractAddressPreimage(address: AztecAddress): Promise<{ + saltedInitializationHash: Fr; + publicKeys: PublicKeys; + currentContractClassId: Fr; + originalContractClassId: Fr; + }>; /** Retrieves the preimage of a contract class id from the contract classes db. */ getContractClassIdPreimage( @@ -29,16 +33,16 @@ export interface ProvingDataOracle { ): Promise<{ artifactHash: Fr; publicBytecodeCommitment: Fr; privateFunctionsRoot: Fr }>; /** - * Retrieve the function membership witness for the given contract address and function selector. + * Retrieve the function membership witness for the given contract class and function selector. * The function membership witness represents a proof that the function belongs to the specified contract. * Throws an error if the contract address or function selector is unknown. * - * @param contractAddress - The contract address. + * @param contractClassId - The id of the class. * @param selector - The function selector. * @returns A promise that resolves with the MembershipWitness instance for the specified contract's function. */ getFunctionMembershipWitness( - contractAddress: AztecAddress, + contractClassId: Fr, selector: FunctionSelector, ): Promise>; @@ -80,4 +84,6 @@ export interface ProvingDataOracle { getMasterSecretKey(masterPublicKey: Point): Promise; getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise; + + getUpdatedClassIdHints(contractAddress: AztecAddress): Promise; } diff --git a/yarn-project/pxe/src/note_decryption_utils/add_public_values_to_payload.ts b/yarn-project/pxe/src/note_decryption_utils/add_public_values_to_payload.ts index 1ec8073784f..e83f76cf8b1 100644 --- a/yarn-project/pxe/src/note_decryption_utils/add_public_values_to_payload.ts +++ b/yarn-project/pxe/src/note_decryption_utils/add_public_values_to_payload.ts @@ -24,10 +24,10 @@ export async function getOrderedNoteItems( ); } - const artifact = await db.getContractArtifact(instance.contractClassId); + const artifact = await db.getContractArtifact(instance.currentContractClassId); if (!artifact) { throw new Error( - `Could not find artifact for contract class ${instance.contractClassId.toString()}. This should never happen here as the partial notes flow should be triggered only for non-deferred notes.`, + `Could not find artifact for contract class ${instance.currentContractClassId.toString()}. This should never happen here as the partial notes flow should be triggered only for non-deferred notes.`, ); } diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 43942bf99fb..aae22071d09 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -252,10 +252,10 @@ export class PXEService implements PXE { if (artifact) { // If the user provides an artifact, validate it against the expected class id and register it const contractClass = await getContractClassFromArtifact(artifact); - const contractClassId = await computeContractClassId(contractClass); - if (!contractClassId.equals(instance.contractClassId)) { + const contractClassId = contractClass.id; + if (!contractClassId.equals(instance.currentContractClassId)) { throw new Error( - `Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.contractClassId})`, + `Artifact does not match expected class id (computed ${contractClassId} but instance refers to ${instance.currentContractClassId})`, ); } const computedAddress = await computeContractAddressFromInstance(instance); @@ -274,16 +274,39 @@ export class PXEService implements PXE { await this.node.addContractClass({ ...contractClass, privateFunctions: [], unconstrainedFunctions: [] }); } else { // Otherwise, make sure there is an artifact already registered for that class id - artifact = await this.db.getContractArtifact(instance.contractClassId); + artifact = await this.db.getContractArtifact(instance.currentContractClassId); if (!artifact) { throw new Error( - `Missing contract artifact for class id ${instance.contractClassId} for contract ${instance.address}`, + `Missing contract artifact for class id ${instance.currentContractClassId} for contract ${instance.address}`, ); } } - this.log.info(`Added contract ${artifact.name} at ${instance.address.toString()}`); await this.db.addContractInstance(instance); + this.log.info( + `Added contract ${artifact.name} at ${instance.address.toString()} with class ${instance.currentContractClassId}`, + ); + } + + public async updateContract(contractAddress: AztecAddress, artifact: ContractArtifact): Promise { + const currentInstance = await this.db.getContractInstance(contractAddress); + if (!currentInstance) { + throw new Error(`Contract ${contractAddress.toString()} is not registered.`); + } + const contractClass = await getContractClassFromArtifact(artifact); + + await this.db.addContractArtifact(contractClass.id, artifact); + + const publicFunctionSignatures = artifact.functions + .filter(fn => fn.functionType === FunctionType.PUBLIC) + .map(fn => decodeFunctionSignature(fn.name, fn.parameters)); + await this.node.registerContractFunctionSignatures(contractAddress, publicFunctionSignatures); + + // TODO(#10007): Node should get public contract class from the registration event, not from PXE registration + await this.node.addContractClass({ ...contractClass, privateFunctions: [], unconstrainedFunctions: [] }); + currentInstance.currentContractClassId = contractClass.id; + await this.db.addContractInstance(currentInstance); + this.log.info(`Updated contract ${artifact.name} at ${contractAddress.toString()} to class ${contractClass.id}`); } public getContracts(): Promise { @@ -730,19 +753,14 @@ export class PXEService implements PXE { * @param execRequest - The transaction request object containing details of the contract call. * @returns An object containing the contract address, function artifact, and historical tree roots. */ - async #getSimulationParameters(execRequest: FunctionCall | TxExecutionRequest) { + #getSimulationParameters(execRequest: FunctionCall | TxExecutionRequest) { const contractAddress = (execRequest as FunctionCall).to ?? (execRequest as TxExecutionRequest).origin; const functionSelector = (execRequest as FunctionCall).selector ?? (execRequest as TxExecutionRequest).functionSelector; - const functionArtifact = await this.contractDataOracle.getFunctionArtifact(contractAddress, functionSelector); - const debug = await this.contractDataOracle.getFunctionDebugMetadata(contractAddress, functionSelector); return { contractAddress, - functionArtifact: { - ...functionArtifact, - debug, - }, + functionSelector, }; } @@ -752,11 +770,11 @@ export class PXEService implements PXE { scopes?: AztecAddress[], ): Promise { // TODO - Pause syncing while simulating. - const { contractAddress, functionArtifact } = await this.#getSimulationParameters(txRequest); + const { contractAddress, functionSelector } = this.#getSimulationParameters(txRequest); try { - const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, msgSender, scopes); - this.log.debug(`Private simulation completed for ${contractAddress.toString()}:${functionArtifact.name}`); + const result = await this.simulator.run(txRequest, contractAddress, functionSelector, msgSender, scopes); + this.log.debug(`Private simulation completed for ${contractAddress.toString()}:${functionSelector}`); return result; } catch (err) { if (err instanceof SimulationError) { @@ -776,12 +794,12 @@ export class PXEService implements PXE { * @returns The simulation result containing the outputs of the unconstrained function. */ async #simulateUnconstrained(execRequest: FunctionCall, scopes?: AztecAddress[]) { - const { contractAddress, functionArtifact } = await this.#getSimulationParameters(execRequest); + const { contractAddress, functionSelector } = this.#getSimulationParameters(execRequest); this.log.debug('Executing unconstrained simulator...'); try { - const result = await this.simulator.runUnconstrained(execRequest, functionArtifact, contractAddress, scopes); - this.log.verbose(`Unconstrained simulation for ${contractAddress}.${functionArtifact.name} completed`); + const result = await this.simulator.runUnconstrained(execRequest, contractAddress, functionSelector, scopes); + this.log.verbose(`Unconstrained simulation for ${contractAddress}.${functionSelector} completed`); return result; } catch (err) { diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 2220f41fdee..c37e4ce2444 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -142,7 +142,7 @@ export class SimulatorOracle implements DBOracle { functionName: string, ): Promise { const instance = await this.contractDataOracle.getContractInstance(contractAddress); - const artifact = await this.contractDataOracle.getContractArtifact(instance.contractClassId); + const artifact = await this.contractDataOracle.getContractArtifact(instance.currentContractClassId); return artifact && getFunctionArtifact(artifact, functionName); } @@ -806,10 +806,11 @@ export class SimulatorOracle implements DBOracle { ); } + const selector = await FunctionSelector.fromNameAndParameters(artifact); const execRequest: FunctionCall = { name: artifact.name, to: contractAddress, - selector: await FunctionSelector.fromNameAndParameters(artifact), + selector, type: FunctionType.UNCONSTRAINED, isStatic: artifact.isStatic, args: encodeArguments(artifact, [ @@ -827,8 +828,8 @@ export class SimulatorOracle implements DBOracle { getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle) ).runUnconstrained( execRequest, - artifact, contractAddress, + selector, [], // empty scope as this call should not require access to private information ); } 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 3363fb56098..b8eb2f027fc 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -27,7 +27,7 @@ import { computeTaggingSecretPoint, deriveKeys, } from '@aztec/circuits.js'; -import { type FunctionArtifact, FunctionType } from '@aztec/foundation/abi'; +import { type FunctionArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { timesParallel } from '@aztec/foundation/collection'; import { pedersenHash, poseidon2Hash } from '@aztec/foundation/crypto'; import { KeyStore } from '@aztec/key-store'; @@ -554,7 +554,7 @@ describe('Simulator oracle', () => { const contractArtifact = randomContractArtifact(); contractArtifact.functions = [processLogFuncArtifact]; await database.addContractInstance(contractInstance); - await database.addContractArtifact(contractInstance.contractClassId, contractArtifact); + await database.addContractArtifact(contractInstance.currentContractClassId, contractArtifact); contractAddress = contractInstance.address; addNotesSpy = jest.spyOn(database, 'addNotes'); @@ -655,7 +655,12 @@ describe('Simulator oracle', () => { // We test that a call to `processLog` is made with the correct function artifact and contract address expect(runUnconstrainedSpy).toHaveBeenCalledTimes(3); - expect(runUnconstrainedSpy).toHaveBeenCalledWith(expect.anything(), processLogFuncArtifact, contractAddress, []); + expect(runUnconstrainedSpy).toHaveBeenCalledWith( + expect.anything(), + contractAddress, + await FunctionSelector.fromNameAndParameters(processLogFuncArtifact.name, processLogFuncArtifact.parameters), + [], + ); }, 30_000); it('should not store notes that do not belong to us', async () => { diff --git a/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts index d5572b9cae2..20e38a4fb4c 100644 --- a/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/phases_validator.test.ts @@ -32,7 +32,8 @@ describe('PhasesTxValidator', () => { contractDataSource = mock({ getContract: mockFn().mockImplementation(() => { return { - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), }; }), }); @@ -71,7 +72,8 @@ describe('PhasesTxValidator', () => { contractDataSource.getContract.mockImplementationOnce(contractAddress => { if (address.equals(contractAddress)) { return Promise.resolve({ - contractClassId: allowedContractClass, + currentContractClassId: allowedContractClass, + originalContractClassId: Fr.random(), } as any); } else { return Promise.resolve(undefined); @@ -94,7 +96,8 @@ describe('PhasesTxValidator', () => { contractDataSource.getContract.mockImplementationOnce(contractAddress => { if (address.equals(contractAddress)) { return Promise.resolve({ - contractClassId: Fr.random(), + currentContractClassId: Fr.random(), + originalContractClassId: Fr.random(), } as any); } else { return Promise.resolve(undefined); diff --git a/yarn-project/sequencer-client/src/tx_validator/phases_validator.ts b/yarn-project/sequencer-client/src/tx_validator/phases_validator.ts index 38112a20ccf..82f021c90f1 100644 --- a/yarn-project/sequencer-client/src/tx_validator/phases_validator.ts +++ b/yarn-project/sequencer-client/src/tx_validator/phases_validator.ts @@ -78,14 +78,14 @@ export class PhasesTxValidator implements TxValidator { } if ('classId' in entry && !('selector' in entry)) { - if (contractClass.contractClassId.equals(entry.classId)) { + if (contractClass.currentContractClassId.equals(entry.classId)) { return true; } } if ('classId' in entry && 'selector' in entry) { if ( - contractClass.contractClassId.equals(entry.classId) && + contractClass.currentContractClassId.equals(entry.classId) && (entry.selector === undefined || entry.selector.equals(functionSelector)) ) { return true; diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 3d2501a3a2e..3f51c67979d 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -58,7 +58,7 @@ export class Oracle { return [ instance.salt, instance.deployer, - instance.contractClassId, + instance.currentContractClassId, instance.initializationHash, ...instance.publicKeys.toFields(), ].map(toACVMField); diff --git a/yarn-project/simulator/src/avm/apps_tests/avm_test.test.ts b/yarn-project/simulator/src/avm/apps_tests/avm_test.test.ts index 1d37b14c418..180a5497dcf 100644 --- a/yarn-project/simulator/src/avm/apps_tests/avm_test.test.ts +++ b/yarn-project/simulator/src/avm/apps_tests/avm_test.test.ts @@ -43,7 +43,7 @@ describe('AVM simulator apps tests: AvmTestContract', () => { argsU8, /*getInstanceForAddress=*/ expectContractInstance.address, /*expectedDeployer=*/ expectContractInstance.deployer, - /*expectedClassId=*/ expectContractInstance.contractClassId, + /*expectedClassId=*/ expectContractInstance.currentContractClassId, /*expectedInitializationHash=*/ expectContractInstance.initializationHash, ]; const results = await simTester.simulateCall(sender, /*address=*/ testContractAddress, 'bulk_testing', args); @@ -61,7 +61,7 @@ describe('AVM simulator apps tests: AvmTestContract', () => { // include another contract address that reuses a class ID to ensure that we can call it even after the limit is reached const instanceSameClassAsFirstContract = await makeContractInstanceFromClassId( - instances[0].contractClassId, + instances[0].currentContractClassId, /*seed=*/ 1000, ); instanceAddresses.push(instanceSameClassAsFirstContract.address); diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index 255420c45a2..0991ba47149 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -864,7 +864,8 @@ describe('AVM simulator: transpiled Noir contracts', () => { version: 1 as const, salt: new Fr(0x123), deployer: AztecAddress.fromBigInt(0x456n), - contractClassId: new Fr(0x789), + currentContractClassId: new Fr(0x789), + originalContractClassId: new Fr(0x789), initializationHash: new Fr(0x101112), publicKeys: new PublicKeys( new Point(new Fr(0x131415), new Fr(0x161718), false), diff --git a/yarn-project/simulator/src/avm/fixtures/simple_contract_data_source.ts b/yarn-project/simulator/src/avm/fixtures/simple_contract_data_source.ts index e76de873ebb..93098ce464d 100644 --- a/yarn-project/simulator/src/avm/fixtures/simple_contract_data_source.ts +++ b/yarn-project/simulator/src/avm/fixtures/simple_contract_data_source.ts @@ -74,8 +74,8 @@ export class SimpleContractDataSource implements ContractDataSource { return undefined; } this.logger.debug(`Retrieved contract artifact for address: ${address}`); - this.logger.debug(`Contract class ID: ${contractInstance.contractClassId}`); - return Promise.resolve(this.contractArtifacts.get(contractInstance!.contractClassId.toString())); + this.logger.debug(`Contract class ID: ${contractInstance.currentContractClassId}`); + return Promise.resolve(this.contractArtifacts.get(contractInstance!.currentContractClassId.toString())); } getContractFunctionName(_address: AztecAddress, _selector: FunctionSelector): Promise { diff --git a/yarn-project/simulator/src/avm/journal/journal.ts b/yarn-project/simulator/src/avm/journal/journal.ts index 3dd20c52eb4..298e3b19855 100644 --- a/yarn-project/simulator/src/avm/journal/journal.ts +++ b/yarn-project/simulator/src/avm/journal/journal.ts @@ -1,5 +1,7 @@ import { MerkleTreeId } from '@aztec/circuit-types'; import { + AvmNullifierReadTreeHint, + AvmPublicDataReadTreeHint, AztecAddress, CANONICAL_AUTH_REGISTRY_ADDRESS, DEPLOYER_CONTRACT_ADDRESS, @@ -7,21 +9,28 @@ import { MULTI_CALL_ENTRYPOINT_ADDRESS, NullifierLeafPreimage, type PublicCallRequest, - type PublicDataTreeLeafPreimage, + PublicDataTreeLeafPreimage, REGISTERER_CONTRACT_ADDRESS, ROUTER_ADDRESS, + ScheduledDelayChange, + ScheduledValueChange, SerializableContractInstance, + UPDATED_CLASS_IDS_SLOT, + computeSharedMutableHashSlot, } from '@aztec/circuits.js'; import { computeNoteHashNonce, computePublicDataTreeLeafSlot, computeUniqueNoteHash, + deriveStorageSlotInMap, siloNoteHash, siloNullifier, } from '@aztec/circuits.js/hash'; +import { poseidon2Hash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { jsonStringify } from '@aztec/foundation/json-rpc'; import { createLogger } from '@aztec/foundation/log'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { strict as assert } from 'assert'; @@ -194,6 +203,26 @@ export class AvmPersistableStateManager { * @returns the latest value written to slot, or 0 if never written to before */ public async readStorage(contractAddress: AztecAddress, slot: Fr): Promise { + const { value, leafPreimage, leafIndex, leafPath } = await this.getPublicDataMembership(contractAddress, slot); + + if (this.doMerkleOperations) { + this.trace.tracePublicStorageRead(contractAddress, slot, value, leafPreimage, leafIndex, leafPath); + } else { + this.trace.tracePublicStorageRead(contractAddress, slot, value); + } + + return Promise.resolve(value); + } + + async getPublicDataMembership( + contractAddress: AztecAddress, + slot: Fr, + ): Promise<{ + value: Fr; + leafPreimage: PublicDataTreeLeafPreimage; + leafIndex: Fr; + leafPath: Fr[]; + }> { const { value, cached } = await this.publicStorage.read(contractAddress, slot); this.log.debug(`Storage read (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`); const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot); @@ -231,14 +260,20 @@ export class AvmPersistableStateManager { 'Public data tree low leaf should skip the target leaf slot when the target leaf does not exist or is the max value.', ); } - // On non-existence, AVM circuit will need to recognize that leafPreimage.slot != leafSlot, - // prove that this is a low leaf that skips leafSlot, and then prove membership of the leaf. - this.trace.tracePublicStorageRead(contractAddress, slot, value, leafPreimage, new Fr(leafIndex), leafPath); + return { + value, + leafPreimage, + leafIndex: new Fr(leafIndex), + leafPath, + }; } else { - this.trace.tracePublicStorageRead(contractAddress, slot, value); + return { + value, + leafPreimage: PublicDataTreeLeafPreimage.empty(), + leafIndex: Fr.ZERO, + leafPath: [], + }; } - - return Promise.resolve(value); } /** @@ -546,24 +581,31 @@ export class AvmPersistableStateManager { const instanceWithAddress = await this.worldStateDB.getContractInstance(contractAddress); const exists = instanceWithAddress !== undefined; - let [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = [ - exists, - NullifierLeafPreimage.empty(), - Fr.ZERO, - new Array(), - ]; + let nullifierMembership = AvmNullifierReadTreeHint.empty(); + let updateMembership = AvmPublicDataReadTreeHint.empty(); + let updatePreimage: Fr[] = []; if (!contractAddressIsCanonical(contractAddress)) { const contractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), contractAddress.toField(), ); - [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership( - /*siloedNullifier=*/ contractAddressNullifier, + const [ + nullifierExistsInTree, + nullifierLeafOrLowLeafPreimage, + nullifierLeafOrLowLeafIndex, + nullifierLeafOrLowLeafPath, + ] = await this.getNullifierMembership(/*siloedNullifier=*/ contractAddressNullifier); + nullifierMembership = new AvmNullifierReadTreeHint( + nullifierLeafOrLowLeafPreimage, + nullifierLeafOrLowLeafIndex, + nullifierLeafOrLowLeafPath, ); assert( - exists == existsInTree, + exists == nullifierExistsInTree, 'WorldStateDB contains contract instance, but nullifier tree does not contain contract address (or vice versa).... This is a bug!', ); + + ({ updateMembership, updatePreimage } = await this.getContractUpdateHints(contractAddress)); } if (exists) { @@ -576,9 +618,9 @@ export class AvmPersistableStateManager { contractAddress, exists, instance, - leafOrLowLeafPreimage, - leafOrLowLeafIndex, - leafOrLowLeafPath, + nullifierMembership, + updateMembership, + updatePreimage, ); } else { this.trace.traceGetContractInstance(contractAddress, exists, instance); @@ -592,9 +634,9 @@ export class AvmPersistableStateManager { contractAddress, exists, /*instance=*/ undefined, - leafOrLowLeafPreimage, - leafOrLowLeafIndex, - leafOrLowLeafPath, + nullifierMembership, + updateMembership, + updatePreimage, ); } else { this.trace.traceGetContractInstance(contractAddress, exists); @@ -611,39 +653,47 @@ export class AvmPersistableStateManager { const instanceWithAddress = await this.worldStateDB.getContractInstance(contractAddress); const exists = instanceWithAddress !== undefined; - let [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = [ - exists, - NullifierLeafPreimage.empty(), - Fr.ZERO, - new Array(), - ]; + let nullifierMembership = AvmNullifierReadTreeHint.empty(); + let updateMembership = AvmPublicDataReadTreeHint.empty(); + let updatePreimage: Fr[] = []; + if (!contractAddressIsCanonical(contractAddress)) { const contractAddressNullifier = await siloNullifier( AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS), contractAddress.toField(), ); - [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership( - /*siloedNullifier=*/ contractAddressNullifier, - ); + const [ + nullifierExistsInTree, + nullifierLeafOrLowLeafPreimage, + nullifierLeafOrLowLeafIndex, + nullifierLeafOrLowLeafPath, + ] = await this.getNullifierMembership(/*siloedNullifier=*/ contractAddressNullifier); assert( - exists == existsInTree, + exists == nullifierExistsInTree, 'WorldStateDB contains contract instance, but nullifier tree does not contain contract address (or vice versa).... This is a bug!', ); + nullifierMembership = new AvmNullifierReadTreeHint( + nullifierLeafOrLowLeafPreimage, + nullifierLeafOrLowLeafIndex, + nullifierLeafOrLowLeafPath, + ); + + ({ updateMembership, updatePreimage } = await this.getContractUpdateHints(contractAddress)); } if (exists) { const instance = new SerializableContractInstance(instanceWithAddress); - const contractClass = await this.worldStateDB.getContractClass(instance.contractClassId); - const bytecodeCommitment = await this.worldStateDB.getBytecodeCommitment(instance.contractClassId); + const contractClass = await this.worldStateDB.getContractClass(instance.currentContractClassId); + const bytecodeCommitment = await this.worldStateDB.getBytecodeCommitment(instance.currentContractClassId); assert( contractClass, - `Contract class not found in DB, but a contract instance was found with this class ID (${instance.contractClassId}). This should not happen!`, + `Contract class not found in DB, but a contract instance was found with this class ID (${instance.currentContractClassId}). This should not happen!`, ); assert( bytecodeCommitment, - `Bytecode commitment was not found in DB for contract class (${instance.contractClassId}). This should not happen!`, + `Bytecode commitment was not found in DB for contract class (${instance.currentContractClassId}). This should not happen!`, ); const contractClassPreimage = { @@ -659,9 +709,9 @@ export class AvmPersistableStateManager { contractClass.packedBytecode, instance, contractClassPreimage, - leafOrLowLeafPreimage, - leafOrLowLeafIndex, - leafOrLowLeafPath, + nullifierMembership, + updateMembership, + updatePreimage, ); } else { this.trace.traceGetBytecode( @@ -685,9 +735,9 @@ export class AvmPersistableStateManager { /*instance=*/ undefined, /*contractClass=*/ undefined, /*bytecode=*/ undefined, - leafOrLowLeafPreimage, - leafOrLowLeafIndex, - leafOrLowLeafPath, + nullifierMembership, + updateMembership, + updatePreimage, ); } else { this.trace.traceGetBytecode(contractAddress, exists); // bytecode, instance, class undefined @@ -696,6 +746,47 @@ export class AvmPersistableStateManager { } } + async getContractUpdateHints(contractAddress: AztecAddress) { + const sharedMutableSlot = await deriveStorageSlotInMap(new Fr(UPDATED_CLASS_IDS_SLOT), contractAddress); + + const hashSlot = await computeSharedMutableHashSlot(sharedMutableSlot); + + const { + value: hash, + leafPreimage, + leafIndex, + leafPath, + } = await this.getPublicDataMembership(ProtocolContractAddress.ContractInstanceDeployer, hashSlot); + const updateMembership = new AvmPublicDataReadTreeHint(leafPreimage, leafIndex, leafPath); + + const readStorage = async (storageSlot: Fr) => + (await this.publicStorage.read(ProtocolContractAddress.ContractInstanceDeployer, storageSlot)).value; + + const valueChange = await ScheduledValueChange.readFromTree(sharedMutableSlot, readStorage); + + const delayChange = await ScheduledDelayChange.readFromTree(sharedMutableSlot, readStorage); + + const updatePreimage = [...valueChange.toFields(), delayChange.toField()]; + + if (!hash.isZero()) { + const hashed = await poseidon2Hash(updatePreimage); + if (!hashed.equals(hash)) { + throw new Error(`Update hint hash mismatch: ${hash} != ${hashed}`); + } + this.log.trace(`Non empty update hint found for contract ${contractAddress}`); + } else { + if (updatePreimage.some(f => !f.isZero())) { + throw new Error(`Update hint hash is zero, but update preimage is not: ${updatePreimage}`); + } + this.log.trace(`No update hint found for contract ${contractAddress}`); + } + + return { + updateMembership, + updatePreimage, + }; + } + public traceEnqueuedCall(publicCallRequest: PublicCallRequest, calldata: Fr[], reverted: boolean) { this.trace.traceEnqueuedCall(publicCallRequest, calldata, reverted); } diff --git a/yarn-project/simulator/src/avm/opcodes/contract.test.ts b/yarn-project/simulator/src/avm/opcodes/contract.test.ts index 9311c9b3278..6532fca0fad 100644 --- a/yarn-project/simulator/src/avm/opcodes/contract.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/contract.test.ts @@ -27,7 +27,7 @@ describe('Contract opcodes', () => { address = await AztecAddress.random(); contractInstance = await SerializableContractInstance.random(); deployer = contractInstance.deployer; - contractClassId = contractInstance.contractClassId; + contractClassId = contractInstance.currentContractClassId; initializationHash = contractInstance.initializationHash; worldStateDB = mock(); trace = mock(); diff --git a/yarn-project/simulator/src/avm/opcodes/contract.ts b/yarn-project/simulator/src/avm/opcodes/contract.ts index f136b1e0bb3..d1cbbc258bc 100644 --- a/yarn-project/simulator/src/avm/opcodes/contract.ts +++ b/yarn-project/simulator/src/avm/opcodes/contract.ts @@ -58,7 +58,7 @@ export class GetContractInstance extends Instruction { memberValue = new Field(instance.deployer.toField()); break; case ContractInstanceMember.CLASS_ID: - memberValue = new Field(instance.contractClassId.toField()); + memberValue = new Field(instance.currentContractClassId.toField()); break; case ContractInstanceMember.INIT_HASH: memberValue = new Field(instance.initializationHash); diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 2f66724b614..7646f16693a 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -31,7 +31,7 @@ import { type SimulationProvider } from '../server.js'; import { type DBOracle } from './db_oracle.js'; import { type ExecutionNoteCache } from './execution_note_cache.js'; import { pickNotes } from './pick_notes.js'; -import { executePrivateFunction } from './private_execution.js'; +import { executePrivateFunction, verifyCurrentClassId } from './private_execution.js'; import { ViewDataOracle } from './view_data_oracle.js'; /** @@ -366,11 +366,18 @@ export class ClientExecutionContext extends ViewDataOracle { isStaticCall: boolean, ) { this.log.debug( - `Calling private function ${this.contractAddress}:${functionSelector} from ${this.callContext.contractAddress}`, + `Calling private function ${targetContractAddress}:${functionSelector} from ${this.callContext.contractAddress}`, ); isStaticCall = isStaticCall || this.callContext.isStaticCall; + await verifyCurrentClassId( + targetContractAddress, + await this.db.getContractInstance(targetContractAddress), + this.node, + this.historicalHeader.globalVariables.blockNumber.toNumber(), + ); + const targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector); const derivedTxContext = this.txContext.clone(); diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index c8797415dd8..2b18beb5506 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -13,6 +13,7 @@ import { BlockHeader, CallContext, CompleteAddress, + type ContractInstance, GasFees, GasSettings, GeneratorIndex, @@ -28,6 +29,7 @@ import { TxContext, computeAppNullifierSecretKey, deriveKeys, + getContractClassFromArtifact, getContractInstanceFromDeployParams, getNonEmptyItems, } from '@aztec/circuits.js'; @@ -40,6 +42,7 @@ import { } from '@aztec/circuits.js/hash'; import { makeHeader } from '@aztec/circuits.js/testing'; import { + type ContractArtifact, type FunctionArtifact, FunctionSelector, type NoteSelector, @@ -65,7 +68,7 @@ import { StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulT import { TestContractArtifact } from '@aztec/noir-contracts.js/Test'; import { jest } from '@jest/globals'; -import { type MockProxy, mock } from 'jest-mock-extended'; +import { Matcher, type MatcherCreator, type MockProxy, mock } from 'jest-mock-extended'; import { toFunctionSelector } from 'viem'; import { MessageLoadOracleInputs } from '../common/message_load_oracle_inputs.js'; @@ -110,31 +113,55 @@ describe('Private Execution test suite', () => { gasSettings: GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }), }; + let contracts: { [address: string]: ContractArtifact }; + + // expectedValue is optional + const aztecAddressMatcher: MatcherCreator = expectedValue => + new Matcher(actualValue => { + return expectedValue?.toString() === actualValue.toString(); + }, 'Matches aztec addresses'); + + const mockContractInstance = async (artifact: ContractArtifact, address: AztecAddress) => { + contracts[address.toString()] = artifact; + const contractClass = await getContractClassFromArtifact(artifact); + + oracle.getContractInstance.calledWith(aztecAddressMatcher(address)).mockResolvedValue({ + currentContractClassId: contractClass.id, + originalContractClassId: contractClass.id, + } as ContractInstance); + }; + const runSimulator = async ({ artifact, + functionName, args = [], msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE), contractAddress = undefined, txContext = {}, }: { - artifact: FunctionArtifact; + artifact: ContractArtifact; + functionName: string; msgSender?: AztecAddress; contractAddress?: AztecAddress; args?: any[]; txContext?: Partial>; }) => { - const hashedArguments = await HashedValues.fromValues(encodeArguments(artifact, args)); + const functionArtifact = getFunctionArtifactByName(artifact, functionName); contractAddress = contractAddress ?? defaultContractAddress; + const selector = await FunctionSelector.fromNameAndParameters(functionName, functionArtifact.parameters); + await mockContractInstance(artifact, contractAddress); + + const hashedArguments = await HashedValues.fromValues(encodeArguments(functionArtifact, args)); const txRequest = TxExecutionRequest.from({ origin: contractAddress, firstCallArgsHash: hashedArguments.hash, - functionSelector: await FunctionSelector.fromNameAndParameters(artifact.name, artifact.parameters), + functionSelector: selector, txContext: TxContext.from({ ...txContextFields, ...txContext }), argsOfCalls: [hashedArguments], authWitnesses: [], }); - return acirSimulator.run(txRequest, artifact, contractAddress, msgSender); + return acirSimulator.run(txRequest, contractAddress, selector, msgSender); }; const insertLeaves = async (leaves: Fr[], name = 'noteHash') => { @@ -205,6 +232,7 @@ describe('Private Execution test suite', () => { beforeEach(async () => { trees = {}; oracle = mock(); + contracts = {}; oracle.getKeyValidationRequest.mockImplementation(async (pkMHash: Fr, contractAddress: AztecAddress) => { if (pkMHash.equals(await ownerCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { return Promise.resolve( @@ -246,6 +274,29 @@ describe('Private Execution test suite', () => { return Promise.resolve(new IndexedTaggingSecret(secret, 0)); }, ); + oracle.getFunctionArtifact.mockImplementation(async (address, selector) => { + const contract = contracts[address.toString()]; + if (!contract) { + throw new Error(`Contract not found: ${address}`); + } + const artifact = await getFunctionArtifact(contract, selector); + if (!artifact) { + throw new Error(`Function not found: ${selector.toString()} in contract ${address}`); + } + return Promise.resolve(artifact); + }); + + oracle.getFunctionArtifactByName.mockImplementation((address, name) => { + const contract = contracts[address.toString()]; + if (!contract) { + throw new Error(`Contract not found: ${address}`); + } + const artifact = getFunctionArtifactByName(contract, name); + if (!artifact) { + throw new Error(`Function not found: ${name} in contract ${address}`); + } + return Promise.resolve(artifact); + }); node = mock(); node.getPublicStorageAt.mockImplementation( @@ -261,10 +312,14 @@ describe('Private Execution test suite', () => { it('emits a field array as an encrypted log', async () => { // NB: this test does NOT cover correct enc/dec of values, just whether // the contexts correctly populate non-note encrypted logs - const artifact = getFunctionArtifactByName(TestContractArtifact, 'emit_array_as_encrypted_log'); const sender = recipient; // Needed for tagging. const args = [times(5, () => Fr.random()), owner, sender, false]; - const result = await runSimulator({ artifact, msgSender: owner, args }); + const result = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'emit_array_as_encrypted_log', + msgSender: owner, + args, + }); const privateLogs = getNonEmptyItems(result.entrypoint.publicInputs.privateLogs); expect(privateLogs).toHaveLength(1); @@ -306,13 +361,8 @@ describe('Private Execution test suite', () => { beforeEach(async () => { contractAddress = await AztecAddress.random(); - oracle.getFunctionArtifactByName.mockImplementation((_, functionName: string) => - Promise.resolve(getFunctionArtifactByName(StatefulTestContractArtifact, functionName)), - ); - oracle.getFunctionArtifact.mockImplementation((_, selector: FunctionSelector) => - Promise.resolve(getFunctionArtifact(StatefulTestContractArtifact, selector)), - ); + await mockContractInstance(StatefulTestContractArtifact, contractAddress); }); it('should have a constructor with arguments that inserts notes', async () => { @@ -321,8 +371,12 @@ describe('Private Execution test suite', () => { constructorArgs: initArgs, }); oracle.getContractInstance.mockResolvedValue(instance); - const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'constructor'); - const executionResult = await runSimulator({ args: initArgs, artifact, contractAddress: instance.address }); + const executionResult = await runSimulator({ + args: initArgs, + artifact: StatefulTestContractArtifact, + functionName: 'constructor', + contractAddress: instance.address, + }); const result = executionResult.entrypoint.nestedExecutions[0]; expect(result.newNotes).toHaveLength(1); @@ -341,9 +395,11 @@ describe('Private Execution test suite', () => { }); it('should run the create_note function', async () => { - const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'create_note_no_init_check'); - - const { entrypoint: result } = await runSimulator({ args: [owner, owner, 140], artifact }); + const { entrypoint: result } = await runSimulator({ + args: [owner, owner, 140], + artifact: StatefulTestContractArtifact, + functionName: 'create_note_no_init_check', + }); expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; @@ -362,7 +418,6 @@ describe('Private Execution test suite', () => { it('should run the destroy_and_create function', async () => { const amountToTransfer = 100n; - const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); const storageSlot = await deriveStorageSlotInMap(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); const recipientStorageSlot = await deriveStorageSlotInMap( @@ -393,7 +448,8 @@ describe('Private Execution test suite', () => { const args = [recipient, amountToTransfer]; const { entrypoint: result, firstNullifier } = await runSimulator({ args, - artifact, + artifact: StatefulTestContractArtifact, + functionName: 'destroy_and_create_no_init_check', msgSender: owner, contractAddress, }); @@ -437,7 +493,6 @@ describe('Private Execution test suite', () => { it('should be able to destroy_and_create with dummy notes', async () => { const amountToTransfer = 100n; const balance = 160n; - const artifact = getFunctionArtifactByName(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); const storageSlot = await deriveStorageSlotInMap(new Fr(1n), owner); @@ -459,7 +514,13 @@ describe('Private Execution test suite', () => { await insertLeaves(consumedNotes.map(n => n.uniqueNoteHash)); const args = [recipient, amountToTransfer]; - const { entrypoint: result } = await runSimulator({ args, artifact, msgSender: owner, contractAddress }); + const { entrypoint: result } = await runSimulator({ + args, + artifact: StatefulTestContractArtifact, + functionName: 'destroy_and_create_no_init_check', + msgSender: owner, + contractAddress, + }); const nullifiers = getNonEmptyItems(result.publicInputs.nullifiers).map(n => n.value); expect(nullifiers).toEqual(consumedNotes.map(n => n.innerNullifier)); @@ -479,30 +540,36 @@ describe('Private Execution test suite', () => { it('child function should be callable', async () => { const initialValue = 100n; - const artifact = getFunctionArtifactByName(ChildContractArtifact, 'value'); - const { entrypoint: result } = await runSimulator({ args: [initialValue], artifact }); + const { entrypoint: result } = await runSimulator({ + args: [initialValue], + artifact: ChildContractArtifact, + functionName: 'value', + }); expect(result.returnValues).toEqual([new Fr(initialValue + privateIncrement)]); }); it('parent should call child', async () => { const childArtifact = getFunctionArtifactByName(ChildContractArtifact, 'value'); - const parentArtifact = getFunctionArtifactByName(ParentContractArtifact, 'entry_point'); const parentAddress = await AztecAddress.random(); const childAddress = await AztecAddress.random(); const childSelector = await FunctionSelector.fromNameAndParameters(childArtifact.name, childArtifact.parameters); - oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(childArtifact)); - + await mockContractInstance(ChildContractArtifact, childAddress); logger.info(`Parent deployed at ${parentAddress.toString()}`); logger.info(`Calling child function ${childSelector.toString()} at ${childAddress.toString()}`); const args = [childAddress, childSelector]; - const { entrypoint: result } = await runSimulator({ args, artifact: parentArtifact }); + const { entrypoint: result } = await runSimulator({ + args, + artifact: ParentContractArtifact, + functionName: 'entry_point', + }); expect(result.returnValues).toEqual([new Fr(privateIncrement)]); - expect(oracle.getFunctionArtifact.mock.calls[0]).toEqual([childAddress, childSelector]); + // First fetch of the function artifact is the parent contract + expect(oracle.getFunctionArtifact.mock.calls[1]).toEqual([childAddress, childSelector]); expect(result.nestedExecutions).toHaveLength(1); expect(result.nestedExecutions[0].returnValues).toEqual([new Fr(privateIncrement)]); expect(result.publicInputs.privateCallRequests[0].callContext).toEqual( @@ -530,27 +597,34 @@ describe('Private Execution test suite', () => { it('test function should be directly callable', async () => { logger.info(`Calling testCodeGen function`); - const { entrypoint: result } = await runSimulator({ args, artifact: testCodeGenArtifact }); + const { entrypoint: result } = await runSimulator({ + args, + artifact: TestContractArtifact, + functionName: 'test_code_gen', + }); expect(result.returnValues).toEqual([argsHash]); }); it('test function should be callable through autogenerated interface', async () => { const testAddress = await AztecAddress.random(); - const parentArtifact = getFunctionArtifactByName(ImportTestContractArtifact, 'main_contract'); const testCodeGenSelector = await FunctionSelector.fromNameAndParameters( testCodeGenArtifact.name, testCodeGenArtifact.parameters, ); - oracle.getFunctionArtifact.mockResolvedValue(testCodeGenArtifact); + await mockContractInstance(TestContractArtifact, testAddress); logger.info(`Calling importer main function`); const args = [testAddress]; - const { entrypoint: result } = await runSimulator({ args, artifact: parentArtifact }); + const { entrypoint: result } = await runSimulator({ + args, + artifact: ImportTestContractArtifact, + functionName: 'main_contract', + }); expect(result.returnValues).toEqual([argsHash]); - expect(oracle.getFunctionArtifact.mock.calls[0]).toEqual([testAddress, testCodeGenSelector]); + expect(oracle.getFunctionArtifact.mock.calls[1]).toEqual([testAddress, testCodeGenSelector]); expect(result.nestedExecutions).toHaveLength(1); expect(result.nestedExecutions[0].returnValues).toEqual([argsHash]); }); @@ -563,7 +637,6 @@ describe('Private Execution test suite', () => { contractAddress = await AztecAddress.random(); }); describe('L1 to L2', () => { - const artifact = getFunctionArtifactByName(TestContractArtifact, 'consume_mint_to_private_message'); let bridgedAmount = 100n; const l1ToL2MessageIndex = 0; @@ -617,7 +690,8 @@ describe('Private Execution test suite', () => { const result = await runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }); @@ -638,7 +712,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }), @@ -659,7 +734,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }), @@ -679,7 +755,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }), @@ -698,7 +775,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(2n) }, }), @@ -717,7 +795,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(2n), chainId: new Fr(1n) }, }), @@ -737,7 +816,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }), @@ -757,7 +837,8 @@ describe('Private Execution test suite', () => { await expect( runSimulator({ contractAddress, - artifact, + artifact: TestContractArtifact, + functionName: 'consume_mint_to_private_message', args, txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }), @@ -766,7 +847,6 @@ describe('Private Execution test suite', () => { }); it('Should be able to consume a dummy public to private message', async () => { - const artifact = getFunctionArtifactByName(TestContractArtifact, 'consume_note_from_secret'); const secret = new Fr(1n); const secretHash = await computeSecretHash(secret); const note = new Note([secretHash]); @@ -785,7 +865,12 @@ describe('Private Execution test suite', () => { }, ]); - const { entrypoint: result } = await runSimulator({ artifact, args: [secret], contractAddress }); + const { entrypoint: result } = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'consume_note_from_secret', + args: [secret], + contractAddress, + }); // Check a nullifier has been inserted. const nullifiers = getNonEmptyItems(result.publicInputs.nullifiers); @@ -799,20 +884,22 @@ describe('Private Execution test suite', () => { describe('enqueued calls', () => { it.each([false, true])('parent should enqueue call to child (internal %p)', async isInternal => { - const parentArtifact = getFunctionArtifactByName(ParentContractArtifact, 'enqueue_call_to_child'); - const childContractArtifact = ChildContractArtifact.functions.find(fn => fn.name === 'public_dispatch')!; - expect(childContractArtifact).toBeDefined(); + const childContractArtifact = structuredClone(ChildContractArtifact); + const childFunctionArtifact = childContractArtifact.functions.find(fn => fn.name === 'public_dispatch')!; + expect(childFunctionArtifact).toBeDefined(); + childFunctionArtifact.isInternal = isInternal; + const childAddress = await AztecAddress.random(); + await mockContractInstance(childContractArtifact, childAddress); const childSelector = await FunctionSelector.fromSignature('pub_set_value(Field)'); const parentAddress = await AztecAddress.random(); - oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve({ ...childContractArtifact, isInternal })); - const args = [childAddress, childSelector, 42n]; const result = await runSimulator({ msgSender: parentAddress, contractAddress: parentAddress, - artifact: parentArtifact, + artifact: ParentContractArtifact, + functionName: 'enqueue_call_to_child', args, }); @@ -835,13 +922,18 @@ describe('Private Execution test suite', () => { describe('setting teardown function', () => { it('should be able to set a teardown function', async () => { - const entrypoint = getFunctionArtifactByName(TestContractArtifact, 'test_setting_teardown'); - const teardown = getFunctionArtifactByName(TestContractArtifact, 'dummy_public_call'); - oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve({ ...teardown })); - const { entrypoint: result } = await runSimulator({ artifact: entrypoint }); + // All public functions get wrapped in a public_dispatch function + const publicDispatch = getFunctionArtifactByName(TestContractArtifact, 'public_dispatch'); + const { entrypoint: result } = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'test_setting_teardown', + }); expect(result.publicTeardownFunctionCall.isEmpty()).toBeFalsy(); expect(result.publicTeardownFunctionCall.callContext.functionSelector).toEqual( - await FunctionSelector.fromNameAndParameters(teardown.name, teardown.parameters), + await FunctionSelector.fromNameAndParameters(publicDispatch.name, publicDispatch.parameters), + ); + expect(result.publicTeardownFunctionCall.args[0]).toEqual( + (await FunctionSelector.fromNameAndParameters('dummy_public_call', [])).toField(), ); }); }); @@ -849,16 +941,22 @@ describe('Private Execution test suite', () => { describe('setting fee payer', () => { it('should default to not being a fee payer', async () => { // arbitrary random function that doesn't set a fee payer - const entrypoint = getFunctionArtifactByName(TestContractArtifact, 'get_this_address'); const contractAddress = await AztecAddress.random(); - const { entrypoint: result } = await runSimulator({ artifact: entrypoint, contractAddress }); + const { entrypoint: result } = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'get_this_address', + contractAddress, + }); expect(result.publicInputs.isFeePayer).toBe(false); }); it('should be able to set a fee payer', async () => { - const entrypoint = getFunctionArtifactByName(TestContractArtifact, 'test_setting_fee_payer'); const contractAddress = await AztecAddress.random(); - const { entrypoint: result } = await runSimulator({ artifact: entrypoint, contractAddress }); + const { entrypoint: result } = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'test_setting_fee_payer', + contractAddress, + }); expect(result.publicInputs.isFeePayer).toBe(true); }); }); @@ -866,13 +964,8 @@ describe('Private Execution test suite', () => { describe('pending note hashes contract', () => { const valueNoteTypeId = PendingNoteHashesContractArtifact.notes['ValueNote'].id; - beforeEach(() => { - oracle.getFunctionArtifact.mockImplementation((_, selector) => - Promise.resolve(getFunctionArtifact(PendingNoteHashesContractArtifact, selector)), - ); - oracle.getFunctionArtifactByName.mockImplementation((_, functionName: string) => - Promise.resolve(getFunctionArtifact(PendingNoteHashesContractArtifact, functionName)), - ); + beforeEach(async () => { + await mockContractInstance(PendingNoteHashesContractArtifact, defaultContractAddress); }); it('should be able to insert, read, and nullify pending note hashes in one call', async () => { @@ -883,16 +976,13 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const contractAddress = await AztecAddress.random(); - const artifact = getFunctionArtifactByName( - PendingNoteHashesContractArtifact, - 'test_insert_then_get_then_nullify_flat', - ); const sender = owner; const args = [amountToTransfer, owner, sender]; const { entrypoint: result } = await runSimulator({ args: args, - artifact: artifact, + artifact: PendingNoteHashesContractArtifact, + functionName: 'test_insert_then_get_then_nullify_flat', contractAddress, }); @@ -944,10 +1034,7 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const contractAddress = await AztecAddress.random(); - const artifact = getFunctionArtifactByName( - PendingNoteHashesContractArtifact, - 'test_insert_then_get_then_nullify_all_in_nested_calls', - ); + const insertArtifact = getFunctionArtifactByName(PendingNoteHashesContractArtifact, 'insert_note'); const getThenNullifyArtifact = getFunctionArtifactByName( @@ -968,7 +1055,8 @@ describe('Private Execution test suite', () => { const args = [amountToTransfer, owner, sender, insertFnSelector.toField(), getThenNullifyFnSelector.toField()]; const { entrypoint: result } = await runSimulator({ args: args, - artifact: artifact, + artifact: PendingNoteHashesContractArtifact, + functionName: 'test_insert_then_get_then_nullify_all_in_nested_calls', contractAddress: contractAddress, }); @@ -1024,13 +1112,12 @@ describe('Private Execution test suite', () => { const contractAddress = await AztecAddress.random(); - const artifact = getFunctionArtifactByName(PendingNoteHashesContractArtifact, 'test_bad_get_then_insert_flat'); - const args = [amountToTransfer, owner]; // This will throw if we read the note before it was inserted await runSimulator({ args: args, - artifact: artifact, + artifact: PendingNoteHashesContractArtifact, + functionName: 'test_bad_get_then_insert_flat', contractAddress, }); }); @@ -1038,32 +1125,31 @@ describe('Private Execution test suite', () => { describe('get master incoming viewing public key', () => { it('gets the public key for an address', async () => { - // Tweak the contract artifact so we can extract return values - const artifact = getFunctionArtifactByName(TestContractArtifact, 'get_master_incoming_viewing_public_key'); - // Generate a partial address, pubkey, and resulting address const completeAddress = await CompleteAddress.random(); const args = [completeAddress.address]; const pubKey = completeAddress.publicKeys.masterIncomingViewingPublicKey; oracle.getCompleteAddress.mockResolvedValue(completeAddress); - const { entrypoint: result } = await runSimulator({ artifact, args }); + const { entrypoint: result } = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'get_master_incoming_viewing_public_key', + args, + }); expect(result.returnValues).toEqual([pubKey.x, pubKey.y]); }); }); describe('Get notes', () => { it('fails if returning no notes', async () => { - const artifact = getFunctionArtifactByName(TestContractArtifact, 'call_get_notes'); - const args = [2n, true]; oracle.syncTaggedLogs.mockResolvedValue(new Map()); oracle.processTaggedLogs.mockResolvedValue(); oracle.getNotes.mockResolvedValue([]); - await expect(() => runSimulator({ artifact, args })).rejects.toThrow( - `Assertion failed: Attempted to read past end of BoundedVec`, - ); + await expect(() => + runSimulator({ artifact: TestContractArtifact, functionName: 'call_get_notes', args }), + ).rejects.toThrow(`Assertion failed: Attempted to read past end of BoundedVec`); }); }); @@ -1071,11 +1157,12 @@ describe('Private Execution test suite', () => { it('this_address should return the current context address', async () => { const contractAddress = await AztecAddress.random(); - // Tweak the contract artifact so we can extract return values - const artifact = getFunctionArtifactByName(TestContractArtifact, 'get_this_address'); - - // Overwrite the oracle return value - const { entrypoint: result } = await runSimulator({ artifact, args: [], contractAddress }); + const { entrypoint: result } = await runSimulator({ + artifact: TestContractArtifact, + functionName: 'get_this_address', + args: [], + contractAddress, + }); expect(result.returnValues).toEqual([contractAddress.toField()]); }); }); @@ -1084,27 +1171,35 @@ describe('Private Execution test suite', () => { let chainId: Fr; let version: Fr; let args: any[]; - let artifact: FunctionArtifact; beforeEach(() => { chainId = Fr.random(); version = Fr.random(); args = [chainId, version]; - - artifact = getFunctionArtifactByName(TestContractArtifact, 'assert_private_global_vars'); - oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(artifact)); }); it('Private global vars are correctly set', async () => { // Chain id and version set in tx context is the same as the ones we pass via args so this should not throw - await runSimulator({ artifact, msgSender: owner, args, txContext: { chainId, version } }); + await runSimulator({ + artifact: TestContractArtifact, + functionName: 'assert_private_global_vars', + msgSender: owner, + args, + txContext: { chainId, version }, + }); }); it('Throws when chainId is incorrectly set', async () => { // We set the chainId in the tx context to a different value than the one we pass via args so the simulator should throw const unexpectedChainId = Fr.random(); await expect(() => - runSimulator({ artifact, msgSender: owner, args, txContext: { chainId: unexpectedChainId, version } }), + runSimulator({ + artifact: TestContractArtifact, + functionName: 'assert_private_global_vars', + msgSender: owner, + args, + txContext: { chainId: unexpectedChainId, version }, + }), ).rejects.toThrow('Invalid chain id'); }); @@ -1112,18 +1207,19 @@ describe('Private Execution test suite', () => { // We set the version in the tx context to a different value than the one we pass via args so the simulator should throw const unexpectedVersion = Fr.random(); await expect(() => - runSimulator({ artifact, msgSender: owner, args, txContext: { chainId, version: unexpectedVersion } }), + runSimulator({ + artifact: TestContractArtifact, + functionName: 'assert_private_global_vars', + msgSender: owner, + args, + txContext: { chainId, version: unexpectedVersion }, + }), ).rejects.toThrow('Invalid version'); }); }); describe('Historical header in private context', () => { - let artifact: FunctionArtifact; - beforeEach(() => { - artifact = getFunctionArtifactByName(TestContractArtifact, 'assert_header_private'); - oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(artifact)); - header = makeHeader(); oracle.getBlockHeader.mockClear(); @@ -1133,14 +1229,21 @@ describe('Private Execution test suite', () => { it('Header is correctly set', async () => { const args = [await header.hash()]; - await runSimulator({ artifact, msgSender: owner, args }); + await runSimulator({ + artifact: TestContractArtifact, + functionName: 'assert_header_private', + msgSender: owner, + args, + }); }); it('Throws when header is not as expected', async () => { const unexpectedHeaderHash = Fr.random(); const args = [unexpectedHeaderHash]; - await expect(() => runSimulator({ artifact, msgSender: owner, args })).rejects.toThrow('Invalid header hash'); + await expect(() => + runSimulator({ artifact: TestContractArtifact, functionName: 'assert_header_private', msgSender: owner, args }), + ).rejects.toThrow('Invalid header hash'); }); }); }); diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index ff15487be8f..c4336560140 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -1,15 +1,20 @@ -import { PrivateCallExecutionResult } from '@aztec/circuit-types'; +import { type AztecNode, PrivateCallExecutionResult } from '@aztec/circuit-types'; import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { + type ContractInstance, Fr, PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, PRIVATE_CONTEXT_INPUTS_LENGTH, PrivateCircuitPublicInputs, + ScheduledValueChange, + UPDATED_CLASS_IDS_SLOT, } from '@aztec/circuits.js'; +import { deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; import { type FunctionArtifact, type FunctionSelector, countArgumentsSize } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { createLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; +import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { fromACVMField, witnessMapToFields } from '../acvm/deserialize.js'; import { type ACVMWitness, Oracle, extractCallStack } from '../acvm/index.js'; @@ -115,3 +120,22 @@ export function extractPrivateCircuitPublicInputs( } return PrivateCircuitPublicInputs.fromFields(returnData); } + +export async function verifyCurrentClassId( + contractAddress: AztecAddress, + instance: ContractInstance, + node: AztecNode, + blockNumber: number, +) { + const sharedMutableSlot = await deriveStorageSlotInMap(new Fr(UPDATED_CLASS_IDS_SLOT), contractAddress); + const valueChange = await ScheduledValueChange.readFromTree(sharedMutableSlot, slot => + node.getPublicStorageAt(ProtocolContractAddress.ContractInstanceDeployer, slot, blockNumber), + ); + let currentClassId = valueChange.getCurrentAt(blockNumber); + if (currentClassId.isZero()) { + currentClassId = instance.originalContractClassId; + } + if (!instance.currentContractClassId.equals(currentClassId)) { + throw new Error(`Contract ${contractAddress} is outdated, current class id is ${currentClassId}`); + } +} diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index 381552e0725..423ebea7159 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -24,7 +24,7 @@ import { type SimulationProvider } from '../common/simulation_provider.js'; import { ClientExecutionContext } from './client_execution_context.js'; import { type DBOracle } from './db_oracle.js'; import { ExecutionNoteCache } from './execution_note_cache.js'; -import { executePrivateFunction } from './private_execution.js'; +import { executePrivateFunction, verifyCurrentClassId } from './private_execution.js'; import { executeUnconstrainedFunction } from './unconstrained_execution.js'; import { ViewDataOracle } from './view_data_oracle.js'; @@ -49,11 +49,21 @@ export class AcirSimulator { */ public async run( request: TxExecutionRequest, - entryPointArtifact: FunctionArtifact, contractAddress: AztecAddress, + selector: FunctionSelector, msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE), scopes?: AztecAddress[], ): Promise { + const header = await this.db.getBlockHeader(); + + await verifyCurrentClassId( + contractAddress, + await this.db.getContractInstance(contractAddress), + this.node, + header.globalVariables.blockNumber.toNumber(), + ); + const entryPointArtifact = await this.db.getFunctionArtifact(contractAddress, selector); + if (entryPointArtifact.functionType !== FunctionType.PRIVATE) { throw new Error(`Cannot run ${entryPointArtifact.functionType} function as private`); } @@ -64,8 +74,6 @@ export class AcirSimulator { ); } - const header = await this.db.getBlockHeader(); - // reserve the first side effect for the tx hash (inserted by the private kernel) const startSideEffectCounter = 1; @@ -120,10 +128,18 @@ export class AcirSimulator { */ public async runUnconstrained( request: FunctionCall, - entryPointArtifact: FunctionArtifact, contractAddress: AztecAddress, + selector: FunctionSelector, scopes?: AztecAddress[], ) { + await verifyCurrentClassId( + contractAddress, + await this.db.getContractInstance(contractAddress), + this.node, + await this.node.getBlockNumber(), + ); + const entryPointArtifact = await this.db.getFunctionArtifact(contractAddress, selector); + if (entryPointArtifact.functionType !== FunctionType.UNCONSTRAINED) { throw new Error(`Cannot run ${entryPointArtifact.functionType} function as unconstrained`); } @@ -188,11 +204,11 @@ export class AcirSimulator { } const extendedNoteItems = note.items.concat(Array(maxNoteFields - note.items.length).fill(Fr.ZERO)); - + const selector = await FunctionSelector.fromNameAndParameters(artifact); const execRequest: FunctionCall = { name: artifact.name, to: contractAddress, - selector: await FunctionSelector.fromNameAndParameters(artifact), + selector, type: FunctionType.UNCONSTRAINED, isStatic: artifact.isStatic, args: encodeArguments(artifact, [ @@ -208,8 +224,8 @@ export class AcirSimulator { const [noteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained( execRequest, - artifact, contractAddress, + selector, // We can omit scopes here, because "compute_note_hash_and_optionally_a_nullifier" does not need access to any notes. )) as bigint[]; diff --git a/yarn-project/simulator/src/client/unconstrained_execution.test.ts b/yarn-project/simulator/src/client/unconstrained_execution.test.ts index 11163cef275..600da35a840 100644 --- a/yarn-project/simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/simulator/src/client/unconstrained_execution.test.ts @@ -1,5 +1,5 @@ import { type AztecNode, type FunctionCall, Note } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress } from '@aztec/circuits.js'; +import { BlockHeader, CompleteAddress, type ContractInstance } from '@aztec/circuits.js'; import { FunctionSelector, FunctionType, encodeArguments } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; @@ -56,6 +56,13 @@ describe('Unconstrained Execution test suite', () => { const notes: Note[] = [...Array(5).fill(buildNote(1n, owner)), ...Array(2).fill(buildNote(2n, owner))]; + node.getBlockNumber.mockResolvedValue(27); + node.getPublicStorageAt.mockResolvedValue(Fr.ZERO); + oracle.getFunctionArtifact.mockResolvedValue(artifact); + oracle.getContractInstance.mockResolvedValue({ + currentContractClassId: new Fr(42), + originalContractClassId: new Fr(42), + } as ContractInstance); oracle.syncTaggedLogs.mockResolvedValue(new Map()); oracle.processTaggedLogs.mockResolvedValue(); oracle.getBlockHeader.mockResolvedValue(BlockHeader.empty()); @@ -82,7 +89,7 @@ describe('Unconstrained Execution test suite', () => { returnTypes: artifact.returnTypes, }; - const result = await acirSimulator.runUnconstrained(execRequest, artifact, await AztecAddress.random()); + const result = await acirSimulator.runUnconstrained(execRequest, contractAddress, FunctionSelector.empty()); expect(result).toEqual(9n); }, 30_000); diff --git a/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts b/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts index 985a63d771c..77a609590b1 100644 --- a/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts +++ b/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts @@ -25,8 +25,9 @@ import { WorldStateDB } from '../public_db_sources.js'; import { type PublicTxResult, PublicTxSimulator } from '../public_tx_simulator.js'; import { createTxForPublicCalls } from './index.js'; -const TIMESTAMP = new Fr(99833); -const DEFAULT_GAS_FEES = new GasFees(2, 3); +export const TIMESTAMP = new Fr(99833); +export const DEFAULT_GAS_FEES = new GasFees(2, 3); +export const DEFAULT_BLOCK_NUMBER = 42; export type TestEnqueuedCall = { address: AztecAddress; @@ -59,6 +60,7 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester { const globals = GlobalVariables.empty(); globals.timestamp = TIMESTAMP; globals.gasFees = DEFAULT_GAS_FEES; + globals.blockNumber = new Fr(DEFAULT_BLOCK_NUMBER); const worldStateDB = new WorldStateDB(this.merkleTrees, this.contractDataSource); const simulator = new PublicTxSimulator(this.merkleTrees, worldStateDB, globals, /*doMerkleOperations=*/ true); diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index c5baef1afe3..69b2a5871b4 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -145,9 +145,11 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { if (!instance) { throw new Error(`Contract ${address.toString()} not found`); } - const contractClass = await this.getContractClass(instance.contractClassId); + const contractClass = await this.getContractClass(instance.currentContractClassId); if (!contractClass) { - throw new Error(`Contract class ${instance.contractClassId.toString()} for ${address.toString()} not found`); + throw new Error( + `Contract class ${instance.currentContractClassId.toString()} for ${address.toString()} not found`, + ); } return contractClass.publicFunctions.find(f => f.selector.equals(selector))?.bytecode; } diff --git a/yarn-project/simulator/src/public/side_effect_trace.test.ts b/yarn-project/simulator/src/public/side_effect_trace.test.ts index 920ddaec34f..eed15075728 100644 --- a/yarn-project/simulator/src/public/side_effect_trace.test.ts +++ b/yarn-project/simulator/src/public/side_effect_trace.test.ts @@ -23,6 +23,7 @@ import { PublicDataUpdateRequest, PublicLog, SerializableContractInstance, + Vector, } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { padArrayEnd } from '@aztec/foundation/collection'; @@ -163,18 +164,42 @@ describe('Public Side Effect Trace', () => { it('Should trace get contract instance', async () => { const instance = await SerializableContractInstance.random(); const { version: _, ...instanceWithoutVersion } = instance; - const lowLeafPreimage = new NullifierLeafPreimage(/*siloedNullifier=*/ address.toField(), Fr.ZERO, 0n); + const initializationLowLeafPreimage = new NullifierLeafPreimage( + /*siloedNullifier=*/ address.toField(), + Fr.ZERO, + 0n, + ); + const initializationMembershipHint = new AvmNullifierReadTreeHint( + initializationLowLeafPreimage, + lowLeafIndex, + lowLeafSiblingPath, + ); + const updateSlot = Fr.random(); + const updateMembershipHint = new AvmPublicDataReadTreeHint( + new PublicDataTreeLeafPreimage(updateSlot, Fr.ZERO, Fr.ZERO, updateSlot.add(new Fr(10n)).toBigInt()), + new Fr(1), + [], + ); + const updatePreimage = [new Fr(1), new Fr(2), new Fr(3), new Fr(4)]; const exists = true; - trace.traceGetContractInstance(address, exists, instance, lowLeafPreimage, lowLeafIndex, lowLeafSiblingPath); + trace.traceGetContractInstance( + address, + exists, + instance, + initializationMembershipHint, + updateMembershipHint, + updatePreimage, + ); expect(trace.getCounter()).toBe(startCounterPlus1); - const membershipHint = new AvmNullifierReadTreeHint(lowLeafPreimage, lowLeafIndex, lowLeafSiblingPath); expect(trace.getAvmCircuitHints().contractInstances.items).toEqual([ { address, exists, ...instanceWithoutVersion, - membershipHint, + initializationMembershipHint, + updateMembershipHint, + updatePreimage: new Vector(updatePreimage), }, ]); }); @@ -187,7 +212,23 @@ describe('Public Side Effect Trace', () => { publicBytecodeCommitment: Fr.random(), }; const { version: _, ...instanceWithoutVersion } = instance; - const lowLeafPreimage = new NullifierLeafPreimage(/*siloedNullifier=*/ address.toField(), Fr.ZERO, 0n); + const initializationLowLeafPreimage = new NullifierLeafPreimage( + /*siloedNullifier=*/ address.toField(), + Fr.ZERO, + 0n, + ); + const initializationMembership = new AvmNullifierReadTreeHint( + initializationLowLeafPreimage, + lowLeafIndex, + lowLeafSiblingPath, + ); + const updateSlot = Fr.random(); + const updateMembershipHint = new AvmPublicDataReadTreeHint( + new PublicDataTreeLeafPreimage(updateSlot, Fr.ZERO, Fr.ZERO, updateSlot.add(new Fr(10n)).toBigInt()), + new Fr(1), + [], + ); + const updatePreimage = [new Fr(1), new Fr(2), new Fr(3), new Fr(4)]; const exists = true; trace.traceGetBytecode( address, @@ -195,16 +236,22 @@ describe('Public Side Effect Trace', () => { bytecode, instance, contractClass, - lowLeafPreimage, - lowLeafIndex, - lowLeafSiblingPath, + initializationMembership, + updateMembershipHint, + updatePreimage, ); - const membershipHint = new AvmNullifierReadTreeHint(lowLeafPreimage, lowLeafIndex, lowLeafSiblingPath); expect(Array.from(trace.getAvmCircuitHints().contractBytecodeHints.values())).toEqual([ { bytecode, - contractInstanceHint: { address, exists, ...instanceWithoutVersion, membershipHint: { ...membershipHint } }, + contractInstanceHint: { + address, + exists, + ...instanceWithoutVersion, + initializationMembershipHint: { ...initializationMembership }, + updateMembershipHint, + updatePreimage: new Vector(updatePreimage), + }, contractClassHint: contractClass, }, ]); @@ -361,7 +408,7 @@ describe('Public Side Effect Trace', () => { const differentAddr = AztecAddress.fromNumber(MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS + 1); const instanceWithSameClassId = await SerializableContractInstance.random({ - contractClassId: firstInstance.contractClassId, + currentContractClassId: firstInstance.currentContractClassId, }); // can re-trace different contract address if it has a duplicate class ID trace.traceGetBytecode(differentAddr, /*exists=*/ true, bytecode, instanceWithSameClassId); @@ -407,6 +454,7 @@ describe('Public Side Effect Trace', () => { let testCounter = startCounter; const leafPreimage = new PublicDataTreeLeafPreimage(slot, value, Fr.ZERO, 0n); const lowLeafPreimage = new NullifierLeafPreimage(utxo, Fr.ZERO, 0n); + const nullifierMembership = new AvmNullifierReadTreeHint(lowLeafPreimage, Fr.ZERO, []); nestedTrace.tracePublicStorageRead(address, slot, value, leafPreimage, Fr.ZERO, []); testCounter++; await nestedTrace.tracePublicStorageWrite( @@ -437,9 +485,9 @@ describe('Public Side Effect Trace', () => { testCounter++; nestedTrace.tracePublicLog(address, log); testCounter++; - nestedTrace.traceGetContractInstance(address, /*exists=*/ true, contractInstance, lowLeafPreimage, Fr.ZERO, []); + nestedTrace.traceGetContractInstance(address, /*exists=*/ true, contractInstance, nullifierMembership); testCounter++; - nestedTrace.traceGetContractInstance(address, /*exists=*/ false, contractInstance, lowLeafPreimage, Fr.ZERO, []); + nestedTrace.traceGetContractInstance(address, /*exists=*/ false, contractInstance, nullifierMembership); testCounter++; trace.merge(nestedTrace, reverted); diff --git a/yarn-project/simulator/src/public/side_effect_trace.ts b/yarn-project/simulator/src/public/side_effect_trace.ts index 20ef38ec211..e70819416c2 100644 --- a/yarn-project/simulator/src/public/side_effect_trace.ts +++ b/yarn-project/simulator/src/public/side_effect_trace.ts @@ -370,21 +370,23 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface { contractAddress: AztecAddress, exists: boolean, instance: SerializableContractInstance = SerializableContractInstance.default(), - lowLeafPreimage: NullifierLeafPreimage = NullifierLeafPreimage.empty(), - lowLeafIndex: Fr = Fr.zero(), - lowLeafPath: Fr[] = emptyNullifierPath(), + nullifierMembershipHint: AvmNullifierReadTreeHint = AvmNullifierReadTreeHint.empty(), + updateMembershipHint: AvmPublicDataReadTreeHint = AvmPublicDataReadTreeHint.empty(), + updatePreimage: Fr[] = [], ) { - const membershipHint = new AvmNullifierReadTreeHint(lowLeafPreimage, lowLeafIndex, lowLeafPath); this.avmCircuitHints.contractInstances.items.push( new AvmContractInstanceHint( contractAddress, exists, instance.salt, instance.deployer, - instance.contractClassId, + instance.currentContractClassId, + instance.originalContractClassId, instance.initializationHash, instance.publicKeys, - membershipHint, + nullifierMembershipHint, + updateMembershipHint, + updatePreimage, ), ); this.log.debug(`CONTRACT_INSTANCE cnt: ${this.sideEffectCounter}`); @@ -404,9 +406,9 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface { privateFunctionsRoot: Fr.zero(), publicBytecodeCommitment: Fr.zero(), }, - lowLeafPreimage: NullifierLeafPreimage = NullifierLeafPreimage.empty(), - lowLeafIndex: Fr = Fr.zero(), - lowLeafPath: Fr[] = emptyNullifierPath(), + nullifierMembershipHint: AvmNullifierReadTreeHint = AvmNullifierReadTreeHint.empty(), + updateMembershipHint: AvmPublicDataReadTreeHint = AvmPublicDataReadTreeHint.empty(), + updatePreimage: Fr[] = [], ) { // FIXME: The way we are hinting contract bytecodes is fundamentally broken. // We are mapping contract class ID to a bytecode hint @@ -416,16 +418,18 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface { // But without that instance hinted, the circuit can't prove that the called contract address // actually corresponds to any class ID. - const membershipHint = new AvmNullifierReadTreeHint(lowLeafPreimage, lowLeafIndex, lowLeafPath); const instance = new AvmContractInstanceHint( contractAddress, exists, contractInstance.salt, contractInstance.deployer, - contractInstance.contractClassId, + contractInstance.currentContractClassId, + contractInstance.originalContractClassId, contractInstance.initializationHash, contractInstance.publicKeys, - membershipHint, + nullifierMembershipHint, + updateMembershipHint, + updatePreimage, ); // Always hint the contract instance separately from the bytecode hint. @@ -445,10 +449,10 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface { // Don't we still need to hint if the class ID already exists? // Because the circuit needs to prove that the called contract address corresponds to the class ID. // To do so, the circuit needs to know the class ID in the - if (this.gotBytecodeFromClassIds.has(contractInstance.contractClassId.toString())) { + if (this.gotBytecodeFromClassIds.has(contractInstance.currentContractClassId.toString())) { // this ensures there are no duplicates this.log.debug( - `Contract class id ${contractInstance.contractClassId.toString()} already exists in previous hints`, + `Contract class id ${contractInstance.currentContractClassId.toString()} already exists in previous hints`, ); return; } @@ -462,7 +466,7 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface { // present/used. That would require more bytecode hashing which is exactly what this limit exists to avoid. if (this.gotBytecodeFromClassIds.size() >= MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS) { this.log.debug( - `Bytecode retrieval failure for contract class ID ${contractInstance.contractClassId.toString()} (limit reached)`, + `Bytecode retrieval failure for contract class ID ${contractInstance.currentContractClassId.toString()} (limit reached)`, ); throw new SideEffectLimitReachedError( 'contract calls to unique class IDs', @@ -472,12 +476,12 @@ export class SideEffectTrace implements PublicSideEffectTraceInterface { this.log.debug(`Tracing bytecode & contract class for bytecode retrieval: class=${jsonStringify(contractClass)}`); this.avmCircuitHints.contractBytecodeHints.set( - contractInstance.contractClassId.toString(), + contractInstance.currentContractClassId.toString(), new AvmContractBytecodeHints(bytecode, instance, contractClass), ); // After adding the bytecode hint, mark the classId as retrieved to avoid duplication. // The above map alone isn't sufficient because we need to check the parent trace's (and its parent) as well. - this.gotBytecodeFromClassIds.add(contractInstance.contractClassId.toString()); + this.gotBytecodeFromClassIds.add(contractInstance.currentContractClassId.toString()); } /** diff --git a/yarn-project/simulator/src/public/side_effect_trace_interface.ts b/yarn-project/simulator/src/public/side_effect_trace_interface.ts index eea7c9efe23..223727aa522 100644 --- a/yarn-project/simulator/src/public/side_effect_trace_interface.ts +++ b/yarn-project/simulator/src/public/side_effect_trace_interface.ts @@ -1,4 +1,6 @@ import { + type AvmNullifierReadTreeHint, + type AvmPublicDataReadTreeHint, type ContractClassIdPreimage, type NullifierLeafPreimage, type PublicCallRequest, @@ -63,9 +65,9 @@ export interface PublicSideEffectTraceInterface { contractAddress: AztecAddress, exists: boolean, instance?: SerializableContractInstance, - lowLeafPreimage?: NullifierLeafPreimage, - lowLeafIndex?: Fr, - lowLeafPath?: Fr[], + nullifierMembershipHint?: AvmNullifierReadTreeHint, + updateMembershipHint?: AvmPublicDataReadTreeHint, + updatePreimage?: Fr[], ): void; traceGetBytecode( contractAddress: AztecAddress, @@ -73,9 +75,9 @@ export interface PublicSideEffectTraceInterface { bytecode?: Buffer, contractInstance?: SerializableContractInstance, contractClass?: ContractClassIdPreimage, - lowLeafPreimage?: NullifierLeafPreimage, - lowLeafIndex?: Fr, - lowLeafPath?: Fr[], + nullifierMembershipHint?: AvmNullifierReadTreeHint, + updateMembershipHint?: AvmPublicDataReadTreeHint, + updatePreimage?: Fr[], ): void; traceEnqueuedCall( /** The call request from private that enqueued this call. */ diff --git a/yarn-project/txe/src/node/txe_node.ts b/yarn-project/txe/src/node/txe_node.ts index 39bf2d1e4cd..570781336d7 100644 --- a/yarn-project/txe/src/node/txe_node.ts +++ b/yarn-project/txe/src/node/txe_node.ts @@ -10,7 +10,7 @@ import { type L2BlockNumber, type L2Tips, type LogFilter, - type MerkleTreeId, + MerkleTreeId, type MerkleTreeReadOperations, type MerkleTreeWriteOperations, type NullifierMembershipWitness, @@ -41,8 +41,10 @@ import { PUBLIC_LOG_DATA_SIZE_IN_FIELDS, type PrivateLog, type ProtocolContractAddresses, + type PublicDataTreeLeafPreimage, type PublicLog, } from '@aztec/circuits.js'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { type L1ContractAddresses } from '@aztec/ethereum'; import { poseidon2Hash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; @@ -583,8 +585,23 @@ export class TXENode implements AztecNode { * @param blockNumber - The block number at which to get the data or 'latest'. * @returns Storage value at the given contract slot. */ - getPublicStorageAt(_contract: AztecAddress, _slot: Fr, _blockNumber: L2BlockNumber): Promise { - throw new Error('TXE Node method getPublicStorageAt not implemented'); + async getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise { + const db: MerkleTreeReadOperations = + blockNumber === (await this.getBlockNumber()) || blockNumber === 'latest' || blockNumber === undefined + ? this.baseFork + : this.nativeWorldStateService.getSnapshot(blockNumber); + + const leafSlot = await computePublicDataTreeLeafSlot(contract, slot); + + const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + if (!lowLeafResult || !lowLeafResult.alreadyPresent) { + return Fr.ZERO; + } + const preimage = (await db.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + return preimage.value; } /** diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index cdd706c457a..24b0983f978 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -866,7 +866,7 @@ export class TXE implements TypedOracle { if (!instance) { return undefined; } - const artifact = await this.contractDataOracle.getContractArtifact(instance!.contractClassId); + const artifact = await this.contractDataOracle.getContractArtifact(instance!.currentContractClassId); if (!artifact) { return undefined; } diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 245bb7e7b5d..45b8632140d 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -122,7 +122,7 @@ export class TXEService { toArray([ instance.salt, instance.deployer.toField(), - instance.contractClassId, + instance.currentContractClassId, instance.initializationHash, ...instance.publicKeys.toFields(), ]), @@ -417,7 +417,7 @@ export class TXEService { toArray([ instance.salt, instance.deployer.toField(), - instance.contractClassId, + instance.currentContractClassId, instance.initializationHash, ...instance.publicKeys.toFields(), ]), @@ -632,7 +632,7 @@ export class TXEService { async avmOpcodeGetContractInstanceClassId(address: ForeignCallSingle) { const instance = await this.typedOracle.getContractInstance(addressFromSingle(address)); return toForeignCallResult([ - toSingle(instance.contractClassId), + toSingle(instance.currentContractClassId), // AVM requires an extra boolean indicating the instance was found toSingle(new Fr(1)), ]); diff --git a/yarn-project/txe/src/util/txe_public_contract_data_source.ts b/yarn-project/txe/src/util/txe_public_contract_data_source.ts index a9cccb092b8..fdb61f26b6a 100644 --- a/yarn-project/txe/src/util/txe_public_contract_data_source.ts +++ b/yarn-project/txe/src/util/txe_public_contract_data_source.ts @@ -71,7 +71,7 @@ export class TXEPublicContractDataSource implements ContractDataSource { async getContractArtifact(address: AztecAddress): Promise { const instance = await this.txeOracle.getContractDataOracle().getContractInstance(address); - return this.txeOracle.getContractDataOracle().getContractArtifact(instance.contractClassId); + return this.txeOracle.getContractDataOracle().getContractArtifact(instance.currentContractClassId); } async getContractFunctionName(address: AztecAddress, selector: FunctionSelector): Promise {