Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs-developers/docs/aztec-nr/standards/escrow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 29 additions & 1 deletion docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -67,10 +71,15 @@ pub fn get_contract_instance_deployer_avm(address: AztecAddress) -> Option<Aztec
Option::none()
}
}
pub fn get_contract_instance_class_id_avm(address: AztecAddress) -> Option<ContractClassId> {
/// 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<ContractClassId> {
// 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand All @@ -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!");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions yarn-project/pxe/src/contract_function_simulator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
BOUNDED_VEC,
BUFFER,
BYTE,
CONTRACT_INSTANCE,
DELIVERY_MODE,
EPHEMERAL_ARRAY,
EVENT_VALIDATION_REQUEST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ export const CONTRACT_INSTANCE: TypeMapping<ContractInstance> = {
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(),
Expand Down
10 changes: 2 additions & 8 deletions yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
enrichPublicSimulationError,
} from '@aztec/pxe/server';
import {
CONTRACT_INSTANCE,
ExecutionNoteCache,
ExecutionTaggingIndexCache,
HashedValuesCache,
Expand Down Expand Up @@ -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();
}

/**
Expand Down
Loading