From f4508ea64c59b0fb593b9be62dc0ec466c07314c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 16 Jun 2026 18:04:01 +0000 Subject: [PATCH] fix: rename contract class getters to reflect origina vs current --- .../docs/aztec-nr/standards/escrow.md | 2 +- .../foundational-topics/contract_creation.md | 2 +- .../docs/resources/migration_notes.md | 28 +++++++++++++++++++ .../macros/functions/initialization_utils.nr | 4 +-- .../aztec/src/oracle/get_contract_instance.nr | 21 ++++++++++---- .../aztec/src/publish_contract_instance.nr | 2 +- .../test/avm_test_contract/src/main.nr | 8 +++--- .../crates/types/src/contract_instance.nr | 13 +++++++-- .../src/contract_function_simulator/index.ts | 1 + .../oracle/oracle_type_mappings.ts | 3 +- .../oracle/txe_oracle_top_level_context.ts | 10 ++----- 11 files changed, 67 insertions(+), 27 deletions(-) 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 2da21d8aac75..ef86586b1bee 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.js] Prefunded local network test accounts are now initializerless The genesis-funded test accounts in the local network (sandbox), returned by `getInitialTestAccountsData()`, are now initializerless Schnorr accounts (`schnorr_initializerless`). An initializerless account has no onchain deployment transaction: its address commits to the signing public key (through `immutables_hash`) and its contract state is materialized locally in the PXE, so these accounts are usable right away. 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(); } /**