diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 9963d2727a37..b08984cb5ebe 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,17 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.js] `AccountManager.create` takes an options bag + +`AccountManager.create` no longer takes `salt` as a positional argument. The trailing `salt?: Salt` parameter has been folded into a new `AccountManagerCreateOptions` bag alongside `immutablesHash` and `deployer`: + +```diff +- AccountManager.create(wallet, secret, accountContract, salt) ++ AccountManager.create(wallet, secret, accountContract, { salt }) +``` + +`immutablesHash` lets callers commit a non-zero immutables hash on the resulting `ContractInstance` (folded into the salted initialization hash, so it affects the derived address). `deployer` overrides the deployer address recorded on the instance (defaults to `AztecAddress.ZERO`). The same `immutablesHash` field is now also threaded through `DeployMethod` / `DeployAccountMethod` so the address derived at deploy time matches the one on `accountManager.getInstance()`. + ### [Aztec.nr] Defining a custom `sync_state` function now requires `AztecConfig` Contracts that previously overrode the default `sync_state` by defining their own function with that name will now get a compile error. Use `AztecConfig::custom_sync_state()` instead. diff --git a/yarn-project/aztec.js/src/account/account_with_secret_key.ts b/yarn-project/aztec.js/src/account/account_with_secret_key.ts index 7bc50132bdec..45ec1bd1adf4 100644 --- a/yarn-project/aztec.js/src/account/account_with_secret_key.ts +++ b/yarn-project/aztec.js/src/account/account_with_secret_key.ts @@ -8,7 +8,7 @@ import { computeAddressSecret, deriveMasterIncomingViewingSecretKey } from '@azt import type { ExecutionPayload, TxExecutionRequest } from '@aztec/stdlib/tx'; import type { CallIntent, IntentInnerHash } from '../utils/authwit.js'; -import type { Account, Salt } from './index.js'; +import type { Account } from './index.js'; /** * Extends {@link BaseAccount} with the encryption private key. Not required for @@ -19,8 +19,6 @@ export class AccountWithSecretKey implements Account { constructor( private account: Account, private secretKey: Fr, - /** Deployment salt for this account contract. */ - public readonly salt: Salt, ) {} createTxExecutionRequest( diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 2e46154323fe..20fb1b43d310 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -67,6 +67,11 @@ export type DeployInstantiationOptions = { universalDeploy?: boolean; /** Public keys mixed into the address. Defaults to PublicKeys.default(). */ publicKeys?: PublicKeys; + /** + * Commitment to the contract's immutable storage values. Folded into the salted initialization + * hash, so a non-zero value affects the derived address. Defaults to `Fr.ZERO`. + */ + immutablesHash?: Fr; }; /** @@ -79,6 +84,11 @@ type SharedInstantiationOptions = { salt?: Fr; /** Public keys mixed into the address. Defaults to `PublicKeys.default()`. */ publicKeys?: PublicKeys; + /** + * Commitment to the contract's immutable storage values. Folded into the salted initialization + * hash, so a non-zero value affects the derived address. Defaults to `Fr.ZERO`. + */ + immutablesHash?: Fr; }; /** @@ -242,6 +252,8 @@ export abstract class DeployMethod; @@ -264,6 +276,7 @@ export abstract class DeployMethod, salt: Fr | undefined, publicKeys: PublicKeys | undefined, + immutablesHash: Fr | undefined, payload: DeployMethodPayload = {}, ) { super(wallet, payload.authWitnesses ?? [], payload.capsules ?? []); @@ -273,6 +286,7 @@ export abstract class DeployMethod( wallet, contract, - { salt, publicKeys, universalDeploy: true }, + { salt, publicKeys, immutablesHash, universalDeploy: true }, payload, ); } if (deployer !== undefined) { - return new BoundDeployMethod(wallet, contract, { salt, publicKeys, deployer }, payload); + return new BoundDeployMethod( + wallet, + contract, + { salt, publicKeys, immutablesHash, deployer }, + payload, + ); } - return new PendingDeployMethod(wallet, contract, { salt, publicKeys }, payload); + return new PendingDeployMethod(wallet, contract, { salt, publicKeys, immutablesHash }, payload); } /** @@ -540,6 +559,7 @@ export abstract class DeployMethod { @@ -676,7 +696,7 @@ export class BoundDeployMethod ex 'BoundDeployMethod requires a non-zero `deployer`; use `UniversalDeployMethod` (`{ universalDeploy: true }`) for universal deploys.', ); } - super(wallet, contract, instantiation.salt, instantiation.publicKeys, payload); + super(wallet, contract, instantiation.salt, instantiation.publicKeys, instantiation.immutablesHash, payload); this.deployer = instantiation.deployer; } @@ -703,7 +723,12 @@ export class BoundDeployMethod ex /** Re-emits this method's `DeployInstantiationOptions` for `with(...)` to consume. */ public cloneInstantiation(): DeployInstantiationOptions { - return { salt: this.salt, publicKeys: this.publicKeys, deployer: this.deployer }; + return { + salt: this.salt, + publicKeys: this.publicKeys, + immutablesHash: this.immutablesHash, + deployer: this.deployer, + }; } } @@ -720,7 +745,7 @@ export class UniversalDeployMethod instantiation: PendingInstantiationOptions = {}, payload: DeployMethodPayload = {}, ) { - super(wallet, contract, instantiation.salt, instantiation.publicKeys, payload); + super(wallet, contract, instantiation.salt, instantiation.publicKeys, instantiation.immutablesHash, payload); } /** @@ -802,7 +832,7 @@ export class PendingDeployMethod /** Re-emits this method's `DeployInstantiationOptions` for `with(...)` to consume. */ public cloneInstantiation(): DeployInstantiationOptions { if (!this.#locked) { - return { salt: this.salt, publicKeys: this.publicKeys }; + return { salt: this.salt, publicKeys: this.publicKeys, immutablesHash: this.immutablesHash }; } return this.#locked.cloneInstantiation(); } @@ -815,7 +845,18 @@ export class PendingDeployMethod #promoteFrom( from: SendInteractionOptionsWithoutWait['from'] | undefined, ): BoundDeployMethod | UniversalDeployMethod { - const { wallet, artifact, postDeployCtor, args, salt, publicKeys, authWitnesses, capsules, extraHashedArgs } = this; + const { + wallet, + artifact, + postDeployCtor, + args, + salt, + publicKeys, + immutablesHash, + authWitnesses, + capsules, + extraHashedArgs, + } = this; const contract: DeployMethodContract = { artifact, postDeployCtor, @@ -827,10 +868,15 @@ export class PendingDeployMethod return new UniversalDeployMethod( wallet, contract, - { salt, publicKeys, universalDeploy: true }, + { salt, publicKeys, immutablesHash, universalDeploy: true }, payload, ); } - return new BoundDeployMethod(wallet, contract, { salt, publicKeys, deployer: from }, payload); + return new BoundDeployMethod( + wallet, + contract, + { salt, publicKeys, immutablesHash, deployer: from }, + payload, + ); } } diff --git a/yarn-project/aztec.js/src/wallet/account_manager.ts b/yarn-project/aztec.js/src/wallet/account_manager.ts index a4d653192940..0cfa9a50eb1d 100644 --- a/yarn-project/aztec.js/src/wallet/account_manager.ts +++ b/yarn-project/aztec.js/src/wallet/account_manager.ts @@ -1,4 +1,5 @@ import { Fr } from '@aztec/foundation/curves/bn254'; +import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { CompleteAddress, type ContractInstanceWithAddress, @@ -13,6 +14,21 @@ import { Contract } from '../contract/contract.js'; import { DeployAccountMethod } from './deploy_account_method.js'; import type { Wallet } from './wallet.js'; +/** + * Optional overrides passed to {@link AccountManager.create}. + */ +export interface AccountManagerCreateOptions { + /** Contract instantiation salt. Defaults to a random `Fr`. */ + salt?: Salt; + /** + * Commitment to the contract's immutable storage values. Folded into the salted initialization + * hash, so a non-zero value affects the derived address. Defaults to `Fr.ZERO`. + */ + immutablesHash?: Fr; + /** Address recorded as the instance deployer. Defaults to `AztecAddress.ZERO`. */ + deployer?: AztecAddress; +} + /** * Manages a user account. Provides methods for calculating the account's address and other related data, * plus a helper to return a preconfigured deploy method. @@ -23,15 +39,16 @@ export class AccountManager { private secretKey: Fr, private accountContract: AccountContract, private instance: ContractInstanceWithAddress, - /** - * Contract instantiation salt for the account contract - */ - public readonly salt: Salt, ) {} - static async create(wallet: Wallet, secretKey: Fr, accountContract: AccountContract, salt?: Salt) { + static async create( + wallet: Wallet, + secretKey: Fr, + accountContract: AccountContract, + opts?: AccountManagerCreateOptions, + ) { const { publicKeys } = await deriveKeys(secretKey); - salt = salt !== undefined ? new Fr(salt) : Fr.random(); + const salt = opts?.salt !== undefined ? new Fr(opts.salt) : Fr.random(); const { constructorName, constructorArgs } = (await accountContract.getInitializationFunctionAndArgs()) ?? { constructorName: undefined, @@ -42,11 +59,13 @@ export class AccountManager { const instance = await getContractInstanceFromInstantiationParams(artifact, { constructorArtifact: constructorName, constructorArgs, - salt: salt, + salt, publicKeys, + deployer: opts?.deployer, + immutablesHash: opts?.immutablesHash, }); - return new AccountManager(wallet, secretKey, accountContract, instance, salt); + return new AccountManager(wallet, secretKey, accountContract, instance); } protected getPublicKeys() { @@ -94,7 +113,7 @@ export class AccountManager { public async getAccount(): Promise { const completeAddress = await this.getCompleteAddress(); const account = this.accountContract.getAccount(completeAddress); - return new AccountWithSecretKey(account, this.secretKey, this.salt); + return new AccountWithSecretKey(account, this.secretKey); } /** @@ -130,7 +149,8 @@ export class AccountManager { this.wallet, artifact, instance => Contract.at(instance.address, artifact, this.wallet), - new Fr(this.salt), + this.instance.salt, + this.instance.immutablesHash, account, constructorArgs, constructorName, diff --git a/yarn-project/aztec.js/src/wallet/deploy_account_method.ts b/yarn-project/aztec.js/src/wallet/deploy_account_method.ts index c711d43cdf61..96e91a7c8a94 100644 --- a/yarn-project/aztec.js/src/wallet/deploy_account_method.ts +++ b/yarn-project/aztec.js/src/wallet/deploy_account_method.ts @@ -85,6 +85,7 @@ export class DeployAccountMethod exte artifact: ContractArtifact, postDeployCtor: (instance: ContractInstanceWithAddress, wallet: Wallet) => TContract, salt: Fr, + immutablesHash: Fr, private account: Account, args: any[] = [], constructorNameOrArtifact?: string | FunctionArtifact, @@ -96,7 +97,7 @@ export class DeployAccountMethod exte wallet, { artifact, postDeployCtor, args, constructorNameOrArtifact }, // Account contracts are always deployed universally. - { salt, universalDeploy: true, publicKeys }, + { salt, universalDeploy: true, publicKeys, immutablesHash }, { authWitnesses, capsules, extraHashedArgs }, ); } @@ -221,6 +222,7 @@ export class DeployAccountMethod exte this.artifact, this.postDeployCtor, this.salt, + this.immutablesHash, this.account, this.args, this.constructorArtifact?.name, diff --git a/yarn-project/cli-wallet/src/utils/wallet.ts b/yarn-project/cli-wallet/src/utils/wallet.ts index 2ace44debe9c..e81e78d1fac3 100644 --- a/yarn-project/cli-wallet/src/utils/wallet.ts +++ b/yarn-project/cli-wallet/src/utils/wallet.ts @@ -138,7 +138,7 @@ export class CLIWallet extends BaseWallet { } private async createAccount(secret: Fr, salt: Fr, contract: AccountContract): Promise { - const accountManager = await AccountManager.create(this, secret, contract, salt); + const accountManager = await AccountManager.create(this, secret, contract, { salt }); const instance = accountManager.getInstance(); const artifact = await contract.getContractArtifact(); diff --git a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts index bd9ec905837b..0858e18e26b3 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts @@ -349,7 +349,7 @@ export class ClientFlowsBenchmark { // Register benchy on the user's Wallet, where we're going to be interacting from const accountManager = await this.userWallet.createAccount({ secret: benchysAccount.getSecretKey(), - salt: new Fr(benchysAccount.salt), + salt: new Fr(benchysAccountManager.getInstance().salt), contract: benchysAccountManager.getAccountContract(), }); return accountManager.address; diff --git a/yarn-project/end-to-end/src/spartan/block_capacity.test.ts b/yarn-project/end-to-end/src/spartan/block_capacity.test.ts index 41f99888907a..b62b3e68d83d 100644 --- a/yarn-project/end-to-end/src/spartan/block_capacity.test.ts +++ b/yarn-project/end-to-end/src/spartan/block_capacity.test.ts @@ -112,7 +112,7 @@ describe('block capacity benchmark', () => { wallet, secret, new SchnorrAccountContract(deriveSigningKey(secret)), - salt, + { salt }, ); const deployMethod = await manager.getDeployMethod(); await deployMethod.send({ diff --git a/yarn-project/end-to-end/src/spartan/n_tps.test.ts b/yarn-project/end-to-end/src/spartan/n_tps.test.ts index e15ced7cb33f..9c43877a5c72 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps.test.ts @@ -319,7 +319,7 @@ describe('sustained N TPS test', () => { wallet, secret, new SchnorrAccountContract(deriveSigningKey(secret)), - salt, + { salt }, ); const deployMethod = await manager.getDeployMethod(); // Explicit gas estimation: BaseWallet's fallback bakes diff --git a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts index 7dd82411b8aa..76190e7a0f2d 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts @@ -315,7 +315,7 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { wallet, secret, new SchnorrAccountContract(deriveSigningKey(secret)), - salt, + { salt }, ); const deployMethod = await manager.getDeployMethod(); await deployMethod.send({ diff --git a/yarn-project/end-to-end/src/test-wallet/test_wallet.ts b/yarn-project/end-to-end/src/test-wallet/test_wallet.ts index 55115681d120..fba928c618d9 100644 --- a/yarn-project/end-to-end/src/test-wallet/test_wallet.ts +++ b/yarn-project/end-to-end/src/test-wallet/test_wallet.ts @@ -221,7 +221,7 @@ export class TestWallet extends BaseWallet { const type = accountData?.type ?? 'schnorr'; const contract = accountData?.contract ?? new SchnorrAccountContract(GrumpkinScalar.random()); - const accountManager = await AccountManager.create(this, secret, contract, salt); + const accountManager = await AccountManager.create(this, secret, contract, { salt }); const instance = accountManager.getInstance(); const artifact = await contract.getContractArtifact(); diff --git a/yarn-project/wallets/src/embedded/embedded_wallet.ts b/yarn-project/wallets/src/embedded/embedded_wallet.ts index 6f427994504e..b89819ac9e7b 100644 --- a/yarn-project/wallets/src/embedded/embedded_wallet.ts +++ b/yarn-project/wallets/src/embedded/embedded_wallet.ts @@ -392,7 +392,7 @@ export class EmbeddedWallet extends BaseWallet { } } - const accountManager = await AccountManager.create(this, secret, contract, salt); + const accountManager = await AccountManager.create(this, secret, contract, { salt }); const instance = accountManager.getInstance(); const existingInstance = await this.pxe.getContractInstance(instance.address);