diff --git a/docs/docs-developers/docs/aztec-nr/standards/escrow.md b/docs/docs-developers/docs/aztec-nr/standards/escrow.md index 26adf054c719..8570b010be90 100644 --- a/docs/docs-developers/docs/aztec-nr/standards/escrow.md +++ b/docs/docs-developers/docs/aztec-nr/standards/escrow.md @@ -55,7 +55,7 @@ pub fn _get_escrow( let escrow_instance = ContractInstance { salt: context.this_address().to_field(), deployer: AztecAddress::from_field(0), - contract_class_id: ContractClassId::from_field(escrow_class_id), + original_contract_class_id: ContractClassId::from_field(escrow_class_id), initialization_hash: 0, public_keys: computed_public_keys, }; diff --git a/docs/docs-developers/docs/foundational-topics/contract_creation.md b/docs/docs-developers/docs/foundational-topics/contract_creation.md index 75eaed56069a..41569c13b766 100644 --- a/docs/docs-developers/docs/foundational-topics/contract_creation.md +++ b/docs/docs-developers/docs/foundational-topics/contract_creation.md @@ -53,7 +53,7 @@ A contract instance includes: - `salt`: User-generated pseudorandom value for uniqueness - `deployer`: Optional address of the contract deployer. Zero for universal deployment -- `contract_class_id`: Identifier of the contract class for this instance +- `original_contract_class_id`: Identifier of the contract class the instance was deployed with. Updating the instance to a new class via the ContractInstanceRegistry does not change this value, since it is part of the address preimage - `initialization_hash`: Hash of the selector and arguments to the constructor - `immutables_hash`: Hash of the contract's compile-time immutable state - `public_keys`: Public keys participating in address derivation (nullifier, incoming viewing, outgoing viewing, tagging, message-signing, and fallback keys). Only the incoming viewing key is held as an elliptic curve point; the other five are held as their `hash_public_key` digests. diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index fcbdfb1b075e..fea5c4407a6a 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,34 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.nr] `ContractInstance.contract_class_id` renamed to `original_contract_class_id` + +The `contract_class_id` field of the `ContractInstance` struct (returned by `get_contract_instance`) has been renamed to `original_contract_class_id`. The struct is the contract's *address preimage*, so this field is the class id the contract was deployed with: for contracts whose class was later updated via the `ContractInstanceRegistry`, it is NOT the class currently executing. The rename makes that explicit. + +**Migration:** + +```diff +let instance = get_contract_instance(address); +- let class_id = instance.contract_class_id; ++ let class_id = instance.original_contract_class_id; +``` + +Note that this value is not available during public execution, which only has access to the _current_ contract class. + +### [Aztec.nr] `get_contract_instance_class_id_avm` renamed to `get_contract_instance_current_class_id_avm` + +The AVM contract-instance class id getter has been renamed to make explicit that it returns the *current* class id, i.e. it reflects updates performed via the `ContractInstanceRegistry`. + +**Migration:** + +```diff +- use aztec::oracle::get_contract_instance::get_contract_instance_class_id_avm; ++ use aztec::oracle::get_contract_instance::get_contract_instance_current_class_id_avm; + +- let class_id = get_contract_instance_class_id_avm(address); ++ let class_id = get_contract_instance_current_class_id_avm(address); +``` + ### [Aztec.nr] `for_each` visits elements in order; removing during iteration no longer supported `CapsuleArray::for_each` and `EphemeralArray::for_each` previously iterated backwards (from the last element to the first) so that the callback could safely remove the current element. They now visit elements in order, from first to last, as is usually expected in other languages. Structurally mutating the array (e.g. via `push` or `remove`) from inside the callback is no longer supported. @@ -32,7 +60,7 @@ let _ = array.clear(); kept.for_each(|_index, value| array.push(value)); ``` -`EphemeralArray`'s are cheap and by nature not persistent though, so in most cases you probably can just work with the new copy instead of going through this hassle. +`EphemeralArray`'s are cheap and by nature not persistent though, so in most cases you probably can just work with the new copy instead of going through this hassle. `CapsuleArray` has no `filter`, so iterate manually, backwards. Removing the current element is safe in a backward loop because it only shifts elements at higher indices: diff --git a/noir-projects/aztec-nr/aztec/src/macros/functions/initialization_utils.nr b/noir-projects/aztec-nr/aztec/src/macros/functions/initialization_utils.nr index d4634b4cf0a2..64d9ddd13daa 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/functions/initialization_utils.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/functions/initialization_utils.nr @@ -148,7 +148,7 @@ fn compute_public_initialization_nullifier(address: AztecAddress) -> Field { ) } -// Used by `create_assert_correct_initializer_args` (you won't find it through searching) +// Called by code injected by the macros in `macros/internals_functions_generation/external/public.nr`. pub fn assert_initialization_matches_address_preimage_public(context: PublicContext) { let address = context.this_address(); let deployer = get_contract_instance_deployer_avm(address).unwrap(); @@ -161,7 +161,7 @@ pub fn assert_initialization_matches_address_preimage_public(context: PublicCont ); } -// Used by `create_assert_correct_initializer_args` (you won't find it through searching) +// Called by code injected by the macros in `macros/internals_functions_generation/external/private.nr`. pub fn assert_initialization_matches_address_preimage_private(context: PrivateContext) { let address = context.this_address(); let instance = get_contract_instance(address); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr index 372729c37a9e..3b2122af042b 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_contract_instance.nr @@ -11,7 +11,7 @@ unconstrained fn get_contract_instance_internal(address: AztecAddress) -> Contra get_contract_instance_oracle(address) } -// NOTE: this is for use in private only +/// Returns `address`'s [`ContractInstance`]. pub fn get_contract_instance(address: AztecAddress) -> ContractInstance { // Safety: The to_address function combines all values in the instance object to produce an address, so by checking // that we get the expected address we validate the entire struct. @@ -30,7 +30,9 @@ struct GetContractInstanceResult { #[oracle(aztec_avm_getContractInstanceDeployer)] unconstrained fn get_contract_instance_deployer_oracle_avm(_address: AztecAddress) -> [GetContractInstanceResult; 1] {} #[oracle(aztec_avm_getContractInstanceClassId)] -unconstrained fn get_contract_instance_class_id_oracle_avm(_address: AztecAddress) -> [GetContractInstanceResult; 1] {} +unconstrained fn get_contract_instance_current_class_id_oracle_avm( + _address: AztecAddress, +) -> [GetContractInstanceResult; 1] {} #[oracle(aztec_avm_getContractInstanceInitializationHash)] unconstrained fn get_contract_instance_initialization_hash_oracle_avm( _address: AztecAddress, @@ -43,8 +45,10 @@ unconstrained fn get_contract_instance_immutables_hash_oracle_avm( unconstrained fn get_contract_instance_deployer_internal_avm(address: AztecAddress) -> [GetContractInstanceResult; 1] { get_contract_instance_deployer_oracle_avm(address) } -unconstrained fn get_contract_instance_class_id_internal_avm(address: AztecAddress) -> [GetContractInstanceResult; 1] { - get_contract_instance_class_id_oracle_avm(address) +unconstrained fn get_contract_instance_current_class_id_internal_avm( + address: AztecAddress, +) -> [GetContractInstanceResult; 1] { + get_contract_instance_current_class_id_oracle_avm(address) } unconstrained fn get_contract_instance_initialization_hash_internal_avm( address: AztecAddress, @@ -67,10 +71,15 @@ pub fn get_contract_instance_deployer_avm(address: AztecAddress) -> Option Option { +/// Returns `address` current contract class, or `Option::none` if unpublished. +/// +/// The current contract class is the one that would be used to determine the code of the contract's functions if it +/// were to be executed in this transaction. This is not necessarily the contract's original class if it has been +/// upgraded via the `ContractInstanceRegistry`, and it could similarly change in the future. +pub fn get_contract_instance_current_class_id_avm(address: AztecAddress) -> Option { // Safety: AVM opcodes are constrained by the AVM itself let GetContractInstanceResult { exists, member } = - unsafe { get_contract_instance_class_id_internal_avm(address)[0] }; + unsafe { get_contract_instance_current_class_id_internal_avm(address)[0] }; if exists { Option::some(ContractClassId::from_field(member)) } else { diff --git a/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr b/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr index 51d70272a948..df5ec4830a5f 100644 --- a/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr +++ b/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr @@ -31,7 +31,7 @@ pub fn publish_contract_instance_for_public_execution(context: &mut PrivateConte // immutables_hash) + 7 (public_keys) + 1 (universal_deploy) = 12. let mut serialized_args = [0; 12]; serialized_args[0] = instance.salt; - serialized_args[1] = instance.contract_class_id.to_field(); + serialized_args[1] = instance.original_contract_class_id.to_field(); serialized_args[2] = instance.initialization_hash; serialized_args[3] = instance.immutables_hash; diff --git a/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr index 02ac4f01cbc5..3e3a614fc3ee 100644 --- a/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr @@ -15,7 +15,7 @@ pub contract AvmTest { use aztec::context::gas::GasOpts; use aztec::macros::{functions::{external, view}, storage::storage}; use aztec::oracle::get_contract_instance::{ - get_contract_instance_class_id_avm, get_contract_instance_deployer_avm, + get_contract_instance_current_class_id_avm, get_contract_instance_deployer_avm, get_contract_instance_immutables_hash_avm, get_contract_instance_initialization_hash_avm, }; use aztec::protocol::abis::function_selector::FunctionSelector; @@ -417,7 +417,7 @@ pub contract AvmTest { #[external("public")] fn test_get_contract_instance(address: AztecAddress) { let deployer = get_contract_instance_deployer_avm(address); - let class_id = get_contract_instance_class_id_avm(address); + let class_id = get_contract_instance_current_class_id_avm(address); let initialization_hash = get_contract_instance_initialization_hash_avm(address); let immutables_hash = get_contract_instance_immutables_hash_avm(address); @@ -459,7 +459,7 @@ pub contract AvmTest { expected_immutables_hash: Field, ) { let deployer = get_contract_instance_deployer_avm(address); - let class_id = get_contract_instance_class_id_avm(address); + let class_id = get_contract_instance_current_class_id_avm(address); let initialization_hash = get_contract_instance_initialization_hash_avm(address); let immutables_hash = get_contract_instance_immutables_hash_avm(address); @@ -476,7 +476,7 @@ pub contract AvmTest { // Get a Protocol Contract and it should exist aztec::oracle::logging::debug_log("Get Contract Instance Protocol Contract Instance"); - let fee_juice_class_id = get_contract_instance_class_id_avm(FEE_JUICE_ADDRESS); + let fee_juice_class_id = get_contract_instance_current_class_id_avm(FEE_JUICE_ADDRESS); assert(fee_juice_class_id.is_some(), "Protocol Contract instance not found when getting CLASS_ID!"); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr b/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr index f96851c0d0a1..0301eea2736e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/contract_instance.nr @@ -6,11 +6,18 @@ use crate::{ }; use std::meta::derive; +/// The complete preimage of an [`AztecAddress`]. +/// +/// All of these values are hashed into the contract's `AztecAddress` (see [`Self::to_address`]), so they are fixed +/// at deployment time and never change. +/// +/// In particular, `original_contract_class_id` is the class the contract was *deployed* with. For upgradeable contracts +/// which utilize the `ContractInstanceRegistry` this is NOT the class currently executing i.e. the 'current' class. #[derive(Deserialize, Eq, Serialize)] pub struct ContractInstance { pub salt: Field, pub deployer: AztecAddress, - pub contract_class_id: ContractClassId, + pub original_contract_class_id: ContractClassId, pub initialization_hash: Field, pub immutables_hash: Field, pub public_keys: PublicKeys, @@ -27,7 +34,7 @@ impl ContractInstance { AztecAddress::compute( self.public_keys, PartialAddress::compute( - self.contract_class_id, + self.original_contract_class_id, self.salt, self.initialization_hash, self.deployer, @@ -52,7 +59,7 @@ mod test { let instance = ContractInstance { salt: 6, deployer: AztecAddress::from_field(12), - contract_class_id: ContractClassId::from_field(13), + original_contract_class_id: ContractClassId::from_field(13), initialization_hash: 156, immutables_hash: 789, public_keys: PublicKeys::default(), diff --git a/yarn-project/pxe/src/contract_function_simulator/index.ts b/yarn-project/pxe/src/contract_function_simulator/index.ts index 0e4bea0a9899..9156da426b40 100644 --- a/yarn-project/pxe/src/contract_function_simulator/index.ts +++ b/yarn-project/pxe/src/contract_function_simulator/index.ts @@ -16,6 +16,7 @@ export { BOUNDED_VEC, BUFFER, BYTE, + CONTRACT_INSTANCE, DELIVERY_MODE, EPHEMERAL_ARRAY, EVENT_VALIDATION_REQUEST, diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.ts index 9704cdd94497..d6165211d721 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_type_mappings.ts @@ -229,7 +229,8 @@ export const CONTRACT_INSTANCE: TypeMapping = { fn: v => [ v.salt, v.deployer.toField(), - v.currentContractClassId, + // Note that the nr side of this struct does not contain the current class, only original + v.originalContractClassId, v.initializationHash, v.immutablesHash, ...v.publicKeys.toFields(), diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 0ffebc718581..101a5a5e1341 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -25,6 +25,7 @@ import { enrichPublicSimulationError, } from '@aztec/pxe/server'; import { + CONTRACT_INSTANCE, ExecutionNoteCache, ExecutionTaggingIndexCache, HashedValuesCache, @@ -286,14 +287,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`); } - return [ - instance.salt, - instance.deployer.toField(), - instance.currentContractClassId, - instance.initializationHash, - instance.immutablesHash, - ...instance.publicKeys.toFields(), - ]; + return CONTRACT_INSTANCE.serialization!.fn(instance).flat(); } /**