From 258ff4a00208be8695e2e59aecc14d6a92eaac1c Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Fri, 15 Mar 2024 06:56:57 -0300 Subject: [PATCH] feat: Use deployer in address computation (#5201) Includes deployer field in contract instance address computation as specced in the yellow paper. --- .../src/core/libraries/ConstantsGen.sol | 2 +- noir-projects/aztec-nr/aztec/src/deploy.nr | 9 +-- .../src/events/instance_deployed.nr | 4 +- .../src/main.nr | 12 ++-- .../types/src/abis/public_call_stack_item.nr | 2 +- .../crates/types/src/address/aztec_address.nr | 16 +++-- .../types/src/address/partial_address.nr | 10 ++- .../types/src/address/public_keys_hash.nr | 3 +- .../src/address/salted_initialization_hash.nr | 16 +++-- .../crates/types/src/constants.nr | 2 +- .../crates/types/src/contract_instance.nr | 3 +- .../types/src/tests/fixtures/contracts.nr | 7 +- .../src/defaults/account_interface.ts | 6 +- .../accounts/src/testing/configuration.ts | 1 + .../accounts/src/testing/create_account.ts | 1 + .../archiver/src/archiver/archiver.ts | 67 +------------------ .../aztec.js/src/account/interface.ts | 8 ++- .../aztec.js/src/account_manager/index.ts | 1 + .../aztec.js/src/contract/deploy_method.ts | 3 +- .../src/deployment/deploy_instance.ts | 21 +++--- yarn-project/aztec.js/src/index.ts | 2 +- .../aztec.js/src/wallet/base_wallet.ts | 3 + yarn-project/circuits.js/src/constants.gen.ts | 2 +- .../contract_address.test.ts.snap | 4 +- .../src/contract/contract_address.test.ts | 31 ++++++--- .../src/contract/contract_address.ts | 10 +-- .../src/contract/contract_instance.ts | 4 ++ .../contract_instance_deployed_event.ts | 16 ++--- yarn-project/cli/src/cmds/add_contract.ts | 2 + yarn-project/cli/src/index.ts | 2 + .../src/e2e_crowdfunding_and_claim.test.ts | 21 ++---- .../src/e2e_deploy_contract.test.ts | 53 +++++++++++++-- .../src/e2e_escrow_contract.test.ts | 18 ++--- yarn-project/end-to-end/src/fixtures/utils.ts | 2 +- .../__snapshots__/noir_test_gen.test.ts.snap | 14 ++-- .../src/noir_test_gen.test.ts | 5 +- .../__snapshots__/index.test.ts.snap | 5 +- .../src/class-registerer/index.ts | 2 +- .../__snapshots__/index.test.ts.snap | 5 +- .../__snapshots__/index.test.ts.snap | 5 +- .../src/simulator/public_executor.ts | 7 +- .../simulator/src/acvm/oracle/oracle.ts | 5 +- .../types/src/contracts/contract_instance.ts | 7 ++ 43 files changed, 225 insertions(+), 194 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 71b296280b7..a2ee980b20f 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -92,7 +92,7 @@ library Constants { uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3; + 0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78; uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20; uint256 internal constant GET_NOTE_ORACLE_RETURN_LENGTH = 23; diff --git a/noir-projects/aztec-nr/aztec/src/deploy.nr b/noir-projects/aztec-nr/aztec/src/deploy.nr index 6e8ee87a4a8..6f75d3edef0 100644 --- a/noir-projects/aztec-nr/aztec/src/deploy.nr +++ b/noir-projects/aztec-nr/aztec/src/deploy.nr @@ -6,12 +6,9 @@ use dep::protocol_types::{address::AztecAddress, abis::function_selector::Functi pub fn deploy_contract(context: &mut PrivateContext, target: AztecAddress) { let instance = get_contract_instance(target); - let mut universal_deploy = false; - if !instance.deployer.is_zero() { - assert( - instance.deployer == context.this_address(), "Deployer address does not match current address" - ); - universal_deploy = true; + let universal_deploy = instance.deployer.is_zero(); + if !universal_deploy { + assert(instance.deployer == context.this_address(), "Deployer address does not match current address"); } // Adapted from noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr diff --git a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr index 1a01b992246..497ff310d2e 100644 --- a/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr +++ b/noir-projects/noir-contracts/contracts/contract_instance_deployer_contract/src/events/instance_deployed.nr @@ -13,7 +13,7 @@ struct ContractInstanceDeployed { initialization_hash: Field, portal_contract_address: EthAddress, public_keys_hash: PublicKeysHash, - universal_deploy: bool, + deployer: AztecAddress, } global CONTRACT_INSTANCE_DEPLOYED_SERIALIZED_SIZE: Field = 9; @@ -29,7 +29,7 @@ impl Serialize for ContractInstanceD self.initialization_hash, self.portal_contract_address.to_field(), self.public_keys_hash.to_field(), - self.universal_deploy as Field, + self.deployer.to_field(), ] } } 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 44d3022ac3f..d991c74c0cf 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 @@ -22,14 +22,18 @@ contract ContractInstanceDeployer { ) { // TODO(@spalladino): assert nullifier_exists silo(contract_class_id, ContractClassRegisterer) - // TODO(#4434) Add deployer field to instance calculation - // let deployer = if universal_deploy { Field::zero() } else { context.msg_sender() }; + let deployer = if universal_deploy { + AztecAddress::zero() + } else { + context.msg_sender() + }; let partial_address = PartialAddress::compute( contract_class_id, salt, initialization_hash, - portal_contract_address + portal_contract_address, + deployer ); let address = AztecAddress::compute(public_keys_hash, partial_address); @@ -45,7 +49,7 @@ contract ContractInstanceDeployer { portal_contract_address, initialization_hash, salt, - universal_deploy, + deployer, version: 1 }; let event_payload = event.serialize(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr index 0311c3a10ad..49b417861aa 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr @@ -69,7 +69,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: true, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item request hash" test - let test_data_call_stack_item_request_hash = 0xedd2f10c0cdf776ee2fff3c799bae6df5771f5013a2d5d7154601dffdcf869; + let test_data_call_stack_item_request_hash = 0x00edd2f10c0cdf776ee2fff3c799bae6df5771f5013a2d5d7154601dffdcf869; assert_eq(call_stack_item.hash(), test_data_call_stack_item_request_hash); } 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 27ccf5c164d..aab51f65593 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 @@ -56,7 +56,8 @@ impl AztecAddress { contract_class_id: ContractClassId, salt: Field, initialization_hash: Field, - portal_contract_address: EthAddress + portal_contract_address: EthAddress, + deployer: AztecAddress ) -> AztecAddress { AztecAddress::compute( PublicKeysHash::compute(pub_key), @@ -64,7 +65,8 @@ impl AztecAddress { contract_class_id, salt, initialization_hash, - portal_contract_address + portal_contract_address, + deployer ) ) } @@ -99,16 +101,19 @@ fn compute_address() { let contract_class_id = ContractClassId::from_field(4); let initialization_hash = 5; let portal_contract_address = EthAddress::from_field(6); + let deployer = AztecAddress::from_field(7); let address = AztecAddress::compute_from_public_key( point, contract_class_id, contract_address_salt, initialization_hash, - portal_contract_address + portal_contract_address, + deployer ); - assert(address.to_field() == 0x2fd71a4f0742364f194dd16d0ae32d2f47845ddc7f5d328f37d4148b565c4123); + let expected_computed_address_from_preimage = 0x027ea2b41ced2ec9a98305984e96dd28518536a4628883ccdc06e38aa8997220; + assert(address.to_field() == expected_computed_address_from_preimage); } #[test] @@ -117,5 +122,6 @@ fn compute_address_from_partial_and_pubkey() { let partial_address = PartialAddress::from_field(3); let address = AztecAddress::compute(PublicKeysHash::compute(point), partial_address); - assert(address.to_field() == 0x0447f893197175723deb223696e2e96dbba1e707ee8507766373558877e74197); + let expected_computed_address_from_partial_and_pubkey = 0x0447f893197175723deb223696e2e96dbba1e707ee8507766373558877e74197; + assert(address.to_field() == expected_computed_address_from_partial_and_pubkey); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr index a8dadd6b642..df67b7f6dc2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr @@ -1,5 +1,8 @@ use crate::{ - address::{eth_address::EthAddress, salted_initialization_hash::SaltedInitializationHash}, + address::{ + eth_address::EthAddress, salted_initialization_hash::SaltedInitializationHash, + aztec_address::AztecAddress +}, constants::GENERATOR_INDEX__PARTIAL_ADDRESS, contract_class_id::ContractClassId, hash::pedersen_hash, traits::ToField }; @@ -24,11 +27,12 @@ impl PartialAddress { contract_class_id: ContractClassId, salt: Field, initialization_hash: Field, - portal_contract_address: EthAddress + portal_contract_address: EthAddress, + deployer: AztecAddress ) -> Self { PartialAddress::compute_from_salted_initialization_hash( contract_class_id, - SaltedInitializationHash::compute(salt, initialization_hash, portal_contract_address) + SaltedInitializationHash::compute(salt, initialization_hash, portal_contract_address, deployer) ) } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/public_keys_hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/public_keys_hash.nr index 4452a32313a..ff75e878f4e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/public_keys_hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/public_keys_hash.nr @@ -62,5 +62,6 @@ impl PublicKeysHash { fn compute_public_keys_hash() { let point = GrumpkinPoint { x: 1, y: 2 }; let actual = PublicKeysHash::compute(point); - assert(actual.to_field() == 0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8); + let expected_public_keys_hash = 0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8; + assert(actual.to_field() == expected_public_keys_hash); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/salted_initialization_hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/salted_initialization_hash.nr index ed0b2b831b7..88af664b86b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/salted_initialization_hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/salted_initialization_hash.nr @@ -1,6 +1,6 @@ use crate::{ - address::eth_address::EthAddress, constants::GENERATOR_INDEX__PARTIAL_ADDRESS, hash::pedersen_hash, - traits::ToField + address::{eth_address::EthAddress, aztec_address::AztecAddress}, + constants::GENERATOR_INDEX__PARTIAL_ADDRESS, hash::pedersen_hash, traits::ToField }; // Salted initialization hash. Used in the computation of a partial address. @@ -19,12 +19,18 @@ impl SaltedInitializationHash { Self { inner: field } } - pub fn compute(salt: Field, initialization_hash: Field, portal_contract_address: EthAddress) -> Self { + pub fn compute( + salt: Field, + initialization_hash: Field, + portal_contract_address: EthAddress, + deployer: AztecAddress + ) -> Self { SaltedInitializationHash::from_field( pedersen_hash( [ salt, initialization_hash, + deployer.to_field(), portal_contract_address.to_field() ], GENERATOR_INDEX__PARTIAL_ADDRESS @@ -32,10 +38,6 @@ impl SaltedInitializationHash { ) } - pub fn to_field(self) -> Field { - self.inner - } - pub fn assert_is_zero(self) { assert(self.to_field() == 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 6e47713b6d2..ca2cacd1670 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -130,7 +130,7 @@ global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af8166354 // CONTRACT INSTANCE CONSTANTS // sha224sum 'struct ContractInstanceDeployed' global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; -global DEPLOYER_CONTRACT_ADDRESS = 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3; +global DEPLOYER_CONTRACT_ADDRESS = 0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78; // NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts // Some are defined here because Noir doesn't yet support globals referencing other globals yet. 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 c2de651967b..9e7fc39d8bc 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 @@ -67,7 +67,8 @@ impl ContractInstance { self.contract_class_id, self.salt, self.initialization_hash, - self.portal_contract_address + self.portal_contract_address, + self.deployer ) ) } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr index 96166f5be41..6aa8598dc16 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixtures/contracts.nr @@ -13,6 +13,7 @@ struct ContractData { public_keys_hash: PublicKeysHash, salted_initialization_hash: SaltedInitializationHash, partial_address: PartialAddress, + deployer: AztecAddress, } // taken from __snapshots__/noir_test_gen.test.ts.snap @@ -26,7 +27,8 @@ global default_contract = ContractData { portal_contract_address: EthAddress { inner: 0x0000000000000000000000000000000000005ba0 }, contract_class_id: ContractClassId { inner: 0x0ce2a998337b1e6da1ac1d802a8bb9e10b7d705d210e61efb9642855009814a6 }, public_keys_hash: PublicKeysHash { inner: 0x000000000000000000000000000000000000000000000000000000000000b26e }, - salted_initialization_hash: SaltedInitializationHash { inner: 0x2003637d02f08887d36dc2f27dd961f51f17e0a8a43dc0268dfcefcd96efc3a4 } + salted_initialization_hash: SaltedInitializationHash { inner: 0x2003637d02f08887d36dc2f27dd961f51f17e0a8a43dc0268dfcefcd96efc3a4 }, + deployer: AztecAddress { inner: 0x0000000000000000000000000000000000000000000000000000000000000000 }, }; // taken from __snapshots__/noir_test_gen.test.ts.snap @@ -40,5 +42,6 @@ global parent_contract = ContractData { portal_contract_address: EthAddress { inner: 0x0000000000000000000000000000000000000913 }, contract_class_id: ContractClassId { inner: 0x1f1f963a350e2c883cc6730c19fc5d5b47a40694d805cbb0720fa76fe295df90 }, public_keys_hash: PublicKeysHash { inner: 0x00000000000000000000000000000000000000000000000000000000000011c1 }, - salted_initialization_hash: SaltedInitializationHash { inner: 0x275e153c147f9cb61a5e7b354e81484696d6a54ffc251dcf4ed8276ab24868d2 } + salted_initialization_hash: SaltedInitializationHash { inner: 0x275e153c147f9cb61a5e7b354e81484696d6a54ffc251dcf4ed8276ab24868d2 }, + deployer: AztecAddress { inner: 0x0000000000000000000000000000000000000000000000000000000000000000 }, }; diff --git a/yarn-project/accounts/src/defaults/account_interface.ts b/yarn-project/accounts/src/defaults/account_interface.ts index c02dd6d5505..16c93093328 100644 --- a/yarn-project/accounts/src/defaults/account_interface.ts +++ b/yarn-project/accounts/src/defaults/account_interface.ts @@ -1,6 +1,6 @@ import { AccountInterface, AuthWitnessProvider, EntrypointInterface, FeeOptions } from '@aztec/aztec.js/account'; import { AuthWitness, FunctionCall, TxExecutionRequest } from '@aztec/circuit-types'; -import { CompleteAddress, Fr } from '@aztec/circuits.js'; +import { AztecAddress, CompleteAddress, Fr } from '@aztec/circuits.js'; import { DefaultAccountEntrypoint } from '@aztec/entrypoints/account'; import { NodeInfo } from '@aztec/types/interfaces'; @@ -35,4 +35,8 @@ export class DefaultAccountInterface implements AccountInterface { getCompleteAddress(): CompleteAddress { return this.address; } + + getAddress(): AztecAddress { + return this.address.address; + } } diff --git a/yarn-project/accounts/src/testing/configuration.ts b/yarn-project/accounts/src/testing/configuration.ts index 99a47c8a942..a6a4b7bfe5c 100644 --- a/yarn-project/accounts/src/testing/configuration.ts +++ b/yarn-project/accounts/src/testing/configuration.ts @@ -66,6 +66,7 @@ export async function deployInitialTestAccounts(pxe: PXE) { contractAddressSalt: x.account.salt, skipClassRegistration: true, skipPublicDeployment: true, + universalDeploy: true, }); await deployMethod.simulate({}); return deployMethod; diff --git a/yarn-project/accounts/src/testing/create_account.ts b/yarn-project/accounts/src/testing/create_account.ts index 04b1af1027d..8f5a8040190 100644 --- a/yarn-project/accounts/src/testing/create_account.ts +++ b/yarn-project/accounts/src/testing/create_account.ts @@ -33,6 +33,7 @@ export async function createAccounts(pxe: PXE, numberOfAccounts = 1): Promise - e.toContractInstance(), - ); + const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs).map(e => e.toContractInstance()); if (contractInstances.length > 0) { contractInstances.forEach(c => this.log(`Storing contract instance at ${c.address.toString()}`)); await this.store.addContractInstances(contractInstances, blockNum); } } - /** - * Stores extended contract data as classes and instances. - * Temporary solution until we source this data from the contract class registerer and instance deployer. - * @param contracts - The extended contract data to be stored. - * @param l2BlockNum - The L2 block number to which the contract data corresponds. - * TODO(palla/purge-old-contract-deploy): Delete this method - */ - async storeContractDataAsClassesAndInstances(contracts: ExtendedContractData[], l2BlockNum: number) { - const classesAndInstances = contracts.map(extendedContractDataToContractClassAndInstance); - await this.store.addContractClasses( - classesAndInstances.map(([c, _]) => c), - l2BlockNum, - ); - await this.store.addContractInstances( - classesAndInstances.map(([_, i]) => i), - l2BlockNum, - ); - } - /** * Stops the archiver. * @returns A promise signalling completion of the stop process. @@ -587,39 +560,3 @@ export class Archiver implements ArchiveSource { return this.store.getContractClassIds(); } } - -/** - * Converts ExtendedContractData into contract classes and instances. - * Note that the conversion is not correct, since there is some data missing from the broadcasted ExtendedContractData. - * The archiver will trust the ids broadcasted instead of trying to recompute them. - * Eventually this function and ExtendedContractData altogether will be removed. - */ -function extendedContractDataToContractClassAndInstance( - data: ExtendedContractData, -): [ContractClassPublic, ContractInstanceWithAddress] { - const contractClass: ContractClass = { - version: 1, - artifactHash: Fr.ZERO, - publicFunctions: data.publicFunctions.map(f => ({ - selector: f.selector, - bytecode: f.bytecode, - isInternal: f.isInternal, - })), - privateFunctions: [], - packedBytecode: data.bytecode, - }; - const contractClassId = data.contractClassId; - const contractInstance: ContractInstance = { - version: 1, - salt: data.saltedInitializationHash, - contractClassId, - initializationHash: data.saltedInitializationHash, - portalContractAddress: data.contractData.portalContractAddress, - publicKeysHash: data.publicKeyHash, - }; - const address = data.contractData.contractAddress; - return [ - { ...contractClass, id: contractClassId, privateFunctionsRoot: Fr.ZERO }, - { ...contractInstance, address }, - ]; -} diff --git a/yarn-project/aztec.js/src/account/interface.ts b/yarn-project/aztec.js/src/account/interface.ts index 32ecce8425e..7f9ec40264b 100644 --- a/yarn-project/aztec.js/src/account/interface.ts +++ b/yarn-project/aztec.js/src/account/interface.ts @@ -1,4 +1,5 @@ import { AuthWitness, CompleteAddress, FunctionCall, TxExecutionRequest } from '@aztec/circuit-types'; +import { AztecAddress } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { FeePaymentMethod } from '../fee/fee_payment_method.js'; @@ -39,9 +40,10 @@ export interface EntrypointInterface { * requests and authorize actions for its corresponding account. */ export interface AccountInterface extends AuthWitnessProvider, EntrypointInterface { - /** - * Returns the complete address for this account. - */ + /** Returns the complete address for this account. */ getCompleteAddress(): CompleteAddress; + + /** Returns the address for this account. */ + getAddress(): AztecAddress; } // docs:end:account-interface diff --git a/yarn-project/aztec.js/src/account_manager/index.ts b/yarn-project/aztec.js/src/account_manager/index.ts index 8349f7b6458..d53c85b587a 100644 --- a/yarn-project/aztec.js/src/account_manager/index.ts +++ b/yarn-project/aztec.js/src/account_manager/index.ts @@ -159,6 +159,7 @@ export class AccountManager { contractAddressSalt: this.salt, skipClassRegistration: true, skipPublicDeployment: true, + universalDeploy: true, }); return new DeployAccountSentTx(wallet, sentTx.getTxHash()); } diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 78121c55ef1..43a8560b922 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -159,7 +159,7 @@ export class DeployMethod extends Bas // Deploy the contract via the instance deployer. if (!options.skipPublicDeployment) { - calls.push(deployInstance(this.wallet, instance, { universalDeploy: options.universalDeploy }).request()); + calls.push(deployInstance(this.wallet, instance).request()); } return calls; @@ -192,6 +192,7 @@ export class DeployMethod extends Bas portalAddress: options.portalContract, publicKey: this.publicKey, constructorArtifact: this.constructorArtifact, + deployer: options.universalDeploy ? AztecAddress.ZERO : this.wallet.getAddress(), }); } return this.instance; diff --git a/yarn-project/aztec.js/src/deployment/deploy_instance.ts b/yarn-project/aztec.js/src/deployment/deploy_instance.ts index 21f49fe07c9..aac32143c17 100644 --- a/yarn-project/aztec.js/src/deployment/deploy_instance.ts +++ b/yarn-project/aztec.js/src/deployment/deploy_instance.ts @@ -8,21 +8,22 @@ import { getDeployerContract } from './protocol_contracts.js'; * Sets up a call to the canonical deployer contract to publicly deploy a contract instance. * @param wallet - The wallet to use for the deployment. * @param instance - The instance to deploy. - * @param opts - Additional options. */ -export function deployInstance( - wallet: Wallet, - instance: ContractInstanceWithAddress, - opts: { /** Set to true to *not* mix in the deployer into the address. */ universalDeploy?: boolean } = {}, -): ContractFunctionInteraction { - const deployer = getDeployerContract(wallet); - const { salt, contractClassId, portalContractAddress, publicKeysHash } = instance; - return deployer.methods.deploy( +export function deployInstance(wallet: Wallet, instance: ContractInstanceWithAddress): ContractFunctionInteraction { + const deployerContract = getDeployerContract(wallet); + const { salt, contractClassId, portalContractAddress, publicKeysHash, deployer } = instance; + const isUniversalDeploy = deployer.isZero(); + if (!isUniversalDeploy && !wallet.getAddress().equals(deployer)) { + throw new Error( + `Expected deployer ${deployer.toString()} does not match sender wallet ${wallet.getAddress().toString()}`, + ); + } + return deployerContract.methods.deploy( salt, contractClassId, instance.initializationHash, portalContractAddress, publicKeysHash, - !!opts.universalDeploy, + isUniversalDeploy, ); } diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index ca8bd77e378..e42fa6f174e 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -69,7 +69,7 @@ export { GlobalVariables, GrumpkinScalar, Point, - getContractInstanceFromDeployParams, + getContractInstanceFromDeployParams, // TODO(@spalladino) This method should be used from within the DeployMethod but not exposed in aztec.js getContractClassFromArtifact, INITIAL_L2_BLOCK_NUM, } from '@aztec/circuits.js'; diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index 832987a741a..3f29c2adca0 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -36,6 +36,9 @@ export abstract class BaseWallet implements Wallet { abstract createAuthWitness(message: Fr): Promise; + getAddress() { + return this.getCompleteAddress().address; + } getContractInstance(address: AztecAddress): Promise { return this.pxe.getContractInstance(address); } diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index e26ba5f0a9a..c81ad117097 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -77,7 +77,7 @@ export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99n; export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631n; -export const DEPLOYER_CONTRACT_ADDRESS = 0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3n; +export const DEPLOYER_CONTRACT_ADDRESS = 0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78n; export const L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 17; export const MAX_NOTE_FIELDS_LENGTH = 20; export const GET_NOTE_ORACLE_RETURN_LENGTH = 23; diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap index 4b47ded0b49..0573b324950 100644 --- a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap @@ -4,10 +4,10 @@ exports[`ContractAddress Address from partial matches Noir 1`] = `"0x0447f893197 exports[`ContractAddress Public key hash matches Noir 1`] = `"0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8"`; -exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x2fd71a4f0742364f194dd16d0ae32d2f47845ddc7f5d328f37d4148b565c4123"`; +exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x027ea2b41ced2ec9a98305984e96dd28518536a4628883ccdc06e38aa8997220"`; exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x18f463b8ec102a089c65276a7dc7ec572b091ca394b5e06c03bfd378cb44187f>`; exports[`ContractAddress computePartialAddress 1`] = `Fr<0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8>`; -exports[`ContractAddress computeSaltedInitializationHash 1`] = `Fr<0x19ec8b496dc08811bd32ed38860938b8612793dd4f39c3b3749b8cd5d2f32d12>`; +exports[`ContractAddress computeSaltedInitializationHash 1`] = `Fr<0x25e70e2b5cf1171b74aa1ab4bf1973859a65949a4c83a5365d71434d2062b2b4>`; 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 36c0ae2f91f..46d6965291e 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.test.ts @@ -1,8 +1,8 @@ import { ABIParameterVisibility, FunctionAbi, FunctionType } from '@aztec/foundation/abi'; import { Fr, Point } from '@aztec/foundation/fields'; -import { setupCustomSnapshotSerializers } from '@aztec/foundation/testing'; +import { setupCustomSnapshotSerializers, updateInlineTestData } from '@aztec/foundation/testing'; -import { EthAddress } from '../index.js'; +import { AztecAddress, EthAddress } from '../index.js'; import { computeContractAddressFromInstance, computeContractAddressFromPartial, @@ -28,6 +28,7 @@ describe('ContractAddress', () => { initializationHash: new Fr(1), salt: new Fr(2), portalContractAddress: EthAddress.fromField(new Fr(3)), + deployer: AztecAddress.fromField(new Fr(4)), }; const result = computeSaltedInitializationHash(mockInstance); expect(result).toMatchSnapshot(); @@ -58,6 +59,7 @@ describe('ContractAddress', () => { const contractClassId = new Fr(4n); const initializationHash = new Fr(5n); const portalContractAddress = EthAddress.fromField(new Fr(6n)); + const deployer = AztecAddress.fromField(new Fr(7)); const address = computeContractAddressFromInstance({ publicKeysHash: computePublicKeysHash(publicKey), @@ -65,13 +67,18 @@ describe('ContractAddress', () => { contractClassId, initializationHash, portalContractAddress, + deployer, version: 1, }).toString(); expect(address).toMatchSnapshot(); - // Value used in "compute_address" test in aztec_address.nr - // console.log("address", address); + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr', + 'expected_computed_address_from_preimage', + address.toString(), + ); }); it('Public key hash matches Noir', () => { @@ -79,8 +86,12 @@ describe('ContractAddress', () => { const hash = computePublicKeysHash(publicKey).toString(); expect(hash).toMatchSnapshot(); - // Value used in "compute_public_keys_hash" test in public_keys_hash.nr - // console.log("hash", hash); + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/address/public_keys_hash.nr', + 'expected_public_keys_hash', + hash.toString(), + ); }); it('Address from partial matches Noir', () => { @@ -89,7 +100,11 @@ describe('ContractAddress', () => { const address = computeContractAddressFromPartial({ publicKey, partialAddress }).toString(); expect(address).toMatchSnapshot(); - // Value used in "compute_address_from_partial_and_pubkey" test in aztec_address.nr - // console.log("address", address); + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr', + 'expected_computed_address_from_partial_and_pubkey', + address.toString(), + ); }); }); diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts index e9892ae461c..2f5e684ac1d 100644 --- a/yarn-project/circuits.js/src/contract/contract_address.ts +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -13,7 +13,7 @@ import { PublicKey } from '../types/public_key.js'; /** * Returns the deployment address for a given contract instance as defined on the [Yellow Paper](../../../../yellow-paper/docs/addresses-and-keys/specification.md). * ``` - * salted_initialization_hash = pedersen([salt, initialization_hash, portal_contract_address as Field], GENERATOR__SALTED_INITIALIZATION_HASH) + * salted_initialization_hash = pedersen([salt, initialization_hash, deployer, portal_contract_address as Field], GENERATOR__SALTED_INITIALIZATION_HASH) * partial_address = pedersen([contract_class_id, salted_initialization_hash], GENERATOR__CONTRACT_PARTIAL_ADDRESS_V1) * address = pedersen([public_keys_hash, partial_address], GENERATOR__CONTRACT_ADDRESS_V1) * ``` @@ -35,7 +35,7 @@ export function computeContractAddressFromInstance( */ export function computePartialAddress( instance: - | Pick + | Pick | { contractClassId: Fr; saltedInitializationHash: Fr }, ): Fr { const saltedInitializationHash = @@ -54,10 +54,12 @@ export function computePartialAddress( * @param instance - Contract instance for which to compute the salted initialization hash. */ export function computeSaltedInitializationHash( - instance: Pick, + instance: Pick, ): Fr { return pedersenHash( - [instance.salt, instance.initializationHash, instance.portalContractAddress].map(x => x.toBuffer()), + [instance.salt, instance.initializationHash, instance.deployer, instance.portalContractAddress].map(x => + x.toBuffer(), + ), 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 e30d12beb08..dd96ec19491 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -1,4 +1,5 @@ import { ContractArtifact, FunctionArtifact, getDefaultInitializer } from '@aztec/foundation/abi'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { ContractInstance, ContractInstanceWithAddress } from '@aztec/types/contracts'; @@ -26,6 +27,7 @@ export function getContractInstanceFromDeployParams( salt?: Fr; publicKey?: PublicKey; portalAddress?: EthAddress; + deployer?: AztecAddress; }, ): ContractInstanceWithAddress { const args = opts.constructorArgs ?? []; @@ -33,6 +35,7 @@ export function getContractInstanceFromDeployParams( const publicKey = opts.publicKey ?? Point.ZERO; const portalContractAddress = opts.portalAddress ?? EthAddress.ZERO; const constructorArtifact = getConstructorArtifact(artifact, opts.constructorArtifact); + const deployer = opts.deployer ?? AztecAddress.ZERO; const contractClass = getContractClassFromArtifact(artifact); const contractClassId = computeContractClassId(contractClass); @@ -45,6 +48,7 @@ export function getContractInstanceFromDeployParams( portalContractAddress, publicKeysHash, salt, + deployer, version: 1, }; diff --git a/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts b/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts index a2fdd4c6704..67404dba4eb 100644 --- a/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts +++ b/yarn-project/circuits.js/src/contract/contract_instance_deployed_event.ts @@ -5,7 +5,7 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader } from '@aztec/foundation/serialize'; import { ContractInstanceWithAddress } from '@aztec/types/contracts'; -import { DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE } from '../constants.gen.js'; +import { DEPLOYER_CONTRACT_ADDRESS, DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE } from '../constants.gen.js'; /** Event emitted from the ContractInstanceDeployer. */ export class ContractInstanceDeployedEvent { @@ -17,20 +17,17 @@ export class ContractInstanceDeployedEvent { public readonly initializationHash: Fr, public readonly portalContractAddress: EthAddress, public readonly publicKeysHash: Fr, - public readonly universalDeploy: boolean, + public readonly deployer: AztecAddress, ) {} static isContractInstanceDeployedEvent(log: Buffer) { return toBigIntBE(log.subarray(0, 32)) == DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE; } - // TODO(@spalladino) We should be loading the singleton address from protocol-contracts, - // but the protocol-contracts package depends on this one, and we cannot have circular dependencies, - // hence we require it as an argument for now. - static fromLogs(logs: { contractAddress: AztecAddress; data: Buffer }[], instanceDeployerAddress: AztecAddress) { + static fromLogs(logs: { contractAddress: AztecAddress; data: Buffer }[]) { return logs .filter(log => ContractInstanceDeployedEvent.isContractInstanceDeployedEvent(log.data)) - .filter(log => log.contractAddress.equals(instanceDeployerAddress)) + .filter(log => log.contractAddress.equals(AztecAddress.fromBigInt(DEPLOYER_CONTRACT_ADDRESS))) .map(log => ContractInstanceDeployedEvent.fromLogData(log.data)); } @@ -47,7 +44,7 @@ export class ContractInstanceDeployedEvent { const initializationHash = reader.readObject(Fr); const portalContractAddress = EthAddress.fromField(reader.readObject(Fr)); const publicKeysHash = reader.readObject(Fr); - const universalDeploy = reader.readObject(Fr).toBool(); + const deployer = reader.readObject(AztecAddress); return new ContractInstanceDeployedEvent( address, @@ -57,7 +54,7 @@ export class ContractInstanceDeployedEvent { initializationHash, portalContractAddress, publicKeysHash, - universalDeploy, + deployer, ); } @@ -74,6 +71,7 @@ export class ContractInstanceDeployedEvent { portalContractAddress: this.portalContractAddress, publicKeysHash: this.publicKeysHash, salt: this.salt, + deployer: this.deployer, }; } } diff --git a/yarn-project/cli/src/cmds/add_contract.ts b/yarn-project/cli/src/cmds/add_contract.ts index 39f2fee8100..34fb9817290 100644 --- a/yarn-project/cli/src/cmds/add_contract.ts +++ b/yarn-project/cli/src/cmds/add_contract.ts @@ -20,6 +20,7 @@ export async function addContract( salt: Fr, publicKey: Point | undefined, portalContract: EthAddress | undefined, + deployer: AztecAddress | undefined, debugLogger: DebugLogger, log: LogFn, ) { @@ -32,6 +33,7 @@ export async function addContract( portalContractAddress: portalContract ?? EthAddress.ZERO, publicKeysHash: computePublicKeysHash(publicKey), address, + deployer: deployer ?? AztecAddress.ZERO, }; const computed = computeContractAddressFromInstance(instance); if (!computed.equals(address)) { diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index c895abb416b..4667f81bba2 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -224,6 +224,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option('--salt ', 'Optional deployment salt', parseFieldFromHexString) .option('-p, --public-key ', 'Optional public key for this contract', parsePublicKey) .option('--portal-address
', 'Optional address to a portal contract on L1', parseEthereumAddress) + .option('--deployer-address
', 'Optional address of the contract deployer', parseAztecAddress) .addOption(pxeOption) .action(async options => { const { addContract } = await import('./cmds/add_contract.js'); @@ -235,6 +236,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { options.salt ?? Fr.ZERO, options.publicKey, options.portalContract, + options.deployerAddress, debugLogger, log, ); diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index b48327dba1d..c92fdcf1a90 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -12,12 +12,11 @@ import { computeAuthWitMessageHash, computeMessageSecretHash, generatePublicKey, - getContractInstanceFromDeployParams, } from '@aztec/aztec.js'; import { computePartialAddress } from '@aztec/circuits.js'; import { InclusionProofsContract } from '@aztec/noir-contracts.js'; import { ClaimContract } from '@aztec/noir-contracts.js/Claim'; -import { CrowdfundingContract, CrowdfundingContractArtifact } from '@aztec/noir-contracts.js/Crowdfunding'; +import { CrowdfundingContract } from '@aztec/noir-contracts.js/Crowdfunding'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; @@ -105,25 +104,17 @@ describe('e2e_crowdfunding_and_claim', () => { crowdfundingPrivateKey = GrumpkinScalar.random(); crowdfundingPublicKey = generatePublicKey(crowdfundingPrivateKey); - const salt = Fr.random(); - const args = [donationToken.address, operatorWallet.getAddress(), deadline]; - const deployInfo = getContractInstanceFromDeployParams(CrowdfundingContractArtifact, { - constructorArgs: args, - salt, - publicKey: crowdfundingPublicKey, - }); - await pxe.registerAccount(crowdfundingPrivateKey, computePartialAddress(deployInfo)); - - crowdfundingContract = await CrowdfundingContract.deployWithPublicKey( + const crowdfundingDeployment = CrowdfundingContract.deployWithPublicKey( crowdfundingPublicKey, operatorWallet, donationToken.address, operatorWallet.getAddress(), deadline, - ) - .send({ contractAddressSalt: salt }) - .deployed(); + ); + const crowdfundingInstance = crowdfundingDeployment.getInstance(); + await pxe.registerAccount(crowdfundingPrivateKey, computePartialAddress(crowdfundingInstance)); + crowdfundingContract = await crowdfundingDeployment.send().deployed(); logger(`Crowdfunding contract deployed at ${crowdfundingContract.address}`); claimContract = await ClaimContract.deploy(operatorWallet, crowdfundingContract.address, rewardToken.address) diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index d74ca9a26a3..b46aac442d3 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -57,7 +57,11 @@ describe('e2e_deploy_contract', () => { it('should deploy a test contract', async () => { const salt = Fr.random(); const publicKey = accounts[0].publicKey; - const deploymentData = getContractInstanceFromDeployParams(TestContractArtifact, { salt, publicKey }); + const deploymentData = getContractInstanceFromDeployParams(TestContractArtifact, { + salt, + publicKey, + deployer: wallet.getAddress(), + }); const deployer = new ContractDeployer(TestContractArtifact, wallet, publicKey); const receipt = await deployer.deploy().send({ contractAddressSalt: salt }).wait({ wallet }); expect(receipt.contract.address).toEqual(deploymentData.address); @@ -302,7 +306,7 @@ describe('e2e_deploy_contract', () => { let initArgs: StatefulContractCtorArgs; let contract: StatefulTestContract; - const deployInstance = async (opts: { constructorName?: string } = {}) => { + const deployInstance = async (opts: { constructorName?: string; deployer?: AztecAddress } = {}) => { const initArgs = [accounts[0].address, 42] as StatefulContractCtorArgs; const salt = Fr.random(); const portalAddress = EthAddress.random(); @@ -313,6 +317,7 @@ describe('e2e_deploy_contract', () => { publicKey, portalAddress, constructorArtifact: opts.constructorName, + deployer: opts.deployer, }); const { address, contractClassId } = instance; logger(`Deploying contract instance at ${address.toString()} class id ${contractClassId.toString()}`); @@ -333,6 +338,7 @@ describe('e2e_deploy_contract', () => { portalAddress: instance.portalContractAddress, publicKey, initArgs, + deployer: opts.deployer, }); expect(registered.address).toEqual(instance.address); const contract = await StatefulTestContract.at(instance.address, wallet); @@ -353,6 +359,7 @@ describe('e2e_deploy_contract', () => { expect(deployed!.portalContractAddress).toEqual(instance.portalContractAddress); expect(deployed!.publicKeysHash).toEqual(instance.publicKeysHash); expect(deployed!.salt).toEqual(instance.salt); + expect(deployed!.deployer).toEqual(instance.deployer); }); it('calls a public function with no init check on the deployed instance', async () => { @@ -448,6 +455,27 @@ describe('e2e_deploy_contract', () => { const deployer = await TestContract.deploy(wallet).send().deployed(); await deployer.methods.deploy_contract(instance.address).send().wait(); }); + + describe('error scenarios in deployment', () => { + it('refuses to call a public function on an undeployed contract', async () => { + const whom = accounts[0].address; + const instance = await registerContract(wallet, StatefulTestContract, { initArgs: [whom, 42] }); + await expect( + instance.methods.increment_public_value_no_init_check(whom, 10).send({ skipPublicSimulation: true }).wait(), + ).rejects.toThrow(/dropped/); + }); + + it('refuses to deploy an instance from a different deployer', () => { + const instance = getContractInstanceFromDeployParams(artifact, { + constructorArgs: [AztecAddress.random(), 42], + deployer: AztecAddress.random(), + }); + expect(() => deployInstance(wallet, instance)).toThrow(/does not match/i); + }); + + // TODO(@spalladino): Implement me! + it('refuses to initialize an instance from a different deployer', async () => {}); + }); }); describe('using the contract deploy method', () => { @@ -470,6 +498,15 @@ describe('e2e_deploy_contract', () => { expect(await contract.methods.get_public_value(owner).view()).toEqual(84n); }, 60_000); + it('publicly universally deploys and initializes a contract', async () => { + const owner = accounts[0]; + const opts = { universalDeploy: true }; + const contract = await StatefulTestContract.deploy(wallet, owner, 42).send(opts).deployed(); + expect(await contract.methods.summed_values(owner).view()).toEqual(42n); + await contract.methods.increment_public_value(owner, 84).send().wait(); + expect(await contract.methods.get_public_value(owner).view()).toEqual(84n); + }, 60_000); + it('publicly deploys and calls a public function from the constructor', async () => { const owner = accounts[0]; const token = await TokenContract.deploy(wallet, owner, 'TOKEN', 'TKN', 18).send().deployed(); @@ -538,15 +575,23 @@ type ContractArtifactClass = { async function registerContract( wallet: Wallet, contractArtifact: ContractArtifactClass, - opts: { salt?: Fr; publicKey?: Point; portalAddress?: EthAddress; initArgs?: any[]; constructorName?: string } = {}, + opts: { + salt?: Fr; + publicKey?: Point; + portalAddress?: EthAddress; + initArgs?: any[]; + constructorName?: string; + deployer?: AztecAddress; + } = {}, ): Promise { - const { salt, publicKey, portalAddress, initArgs, constructorName } = opts; + const { salt, publicKey, portalAddress, initArgs, constructorName, deployer } = opts; const instance = getContractInstanceFromDeployParams(contractArtifact.artifact, { constructorArgs: initArgs ?? [], constructorArtifact: constructorName, salt, publicKey, portalAddress, + deployer, }); await wallet.addContracts([{ artifact: contractArtifact.artifact, instance }]); return contractArtifact.at(instance.address, wallet); diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index 763ad54c6d7..bf6455b69af 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -14,10 +14,9 @@ import { TxStatus, computeMessageSecretHash, generatePublicKey, - getContractInstanceFromDeployParams, } from '@aztec/aztec.js'; import { computePartialAddress } from '@aztec/circuits.js'; -import { EscrowContract, EscrowContractArtifact } from '@aztec/noir-contracts.js/Escrow'; +import { EscrowContract } from '@aztec/noir-contracts.js/Escrow'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { setup } from './fixtures/utils.js'; @@ -57,17 +56,10 @@ describe('e2e_escrow_contract', () => { // Note that we need to register it first if we want to emit an encrypted note for it in the constructor escrowPrivateKey = GrumpkinScalar.random(); escrowPublicKey = generatePublicKey(escrowPrivateKey); - const salt = Fr.random(); - const deployInfo = getContractInstanceFromDeployParams(EscrowContractArtifact, { - constructorArgs: [owner], - salt, - publicKey: escrowPublicKey, - }); - await pxe.registerAccount(escrowPrivateKey, computePartialAddress(deployInfo)); - - escrowContract = await EscrowContract.deployWithPublicKey(escrowPublicKey, wallet, owner) - .send({ contractAddressSalt: salt }) - .deployed(); + const escrowDeployment = EscrowContract.deployWithPublicKey(escrowPublicKey, wallet, owner); + const escrowInstance = escrowDeployment.getInstance(); + await pxe.registerAccount(escrowPrivateKey, computePartialAddress(escrowInstance)); + escrowContract = await escrowDeployment.send().deployed(); logger(`Escrow contract deployed at ${escrowContract.address}`); // Deploy Token contract and mint funds for the escrow contract diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 543bd77b48f..b8fb2036055 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -513,7 +513,7 @@ export async function deployPublicProtocolContracts(deployer: Wallet) { await new BatchCall(deployer, [ (await registerContractClass(deployer, canonicalGasToken.artifact)).request(), - deployInstance(deployer, canonicalGasToken.instance, { universalDeploy: true }).request(), + deployInstance(deployer, canonicalGasToken.instance).request(), ]) .send() .wait(); diff --git a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap index fd7c1bc116b..cd7b3714098 100644 --- a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap +++ b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap @@ -4366,12 +4366,13 @@ exports[`Data generation for noir tests Computes contract info for defaultContra artifact_hash: 0x0000000000000000000000000000000000000000000000000000000000003039, public_bytecode_commitment: 0x129a3438653fe147133b2c274757920e37896305e7664c8c1eb380be3efd5fed, private_functions_root: 0x19a3cc0b714976fb35d58b684ba36e86f82bac8b87517904a2727e5113fb4cba, - address: AztecAddress { inner: 0x25de5f29a6d515e67d0ac85f399098acd03c5d4a4884b8c560aee68014715ef1 }, - partial_address: PartialAddress { inner: 0x1420f3a4c4a589be4ea00c0a131dc00127d566725b21dcd1fb421a724710e66b }, + address: AztecAddress { inner: 0x17f7ff235e2548b437b7ef33cdf96c99346753b27d22787c1aa5287cdbad39ee }, + partial_address: PartialAddress { inner: 0x23a6933a485200a8d34b9929d61868c9635793f878d67ce86a1b1355c0ab0d47 }, portal_contract_address: EthAddress { inner: 0x0000000000000000000000000000000000005ba0 }, contract_class_id: ContractClassId { inner: 0x0ce2a998337b1e6da1ac1d802a8bb9e10b7d705d210e61efb9642855009814a6 }, public_keys_hash: PublicKeysHash { inner: 0x000000000000000000000000000000000000000000000000000000000000b26e }, - salted_initialization_hash: SaltedInitializationHash { inner: 0x2003637d02f08887d36dc2f27dd961f51f17e0a8a43dc0268dfcefcd96efc3a4 } + salted_initialization_hash: SaltedInitializationHash { inner: 0x0b095458845137ebf1e6061c8c0ba1d907241a3b56dc1d3e73d2fea78f04a036 }, + deployer: AztecAddress { inner: 0x0000000000000000000000000000000000000000000000000000000000000000 } }" `; @@ -4381,12 +4382,13 @@ exports[`Data generation for noir tests Computes contract info for parentContrac artifact_hash: 0x00000000000000000000000000000000000000000000000000000000000004bc, public_bytecode_commitment: 0x1435ed970b275bebf95de3df53f23f3d2e97c9b54cf442bb03a3fa17a0ee3cd7, private_functions_root: 0x2c1c949cb226995de94b7b8b5aeaab440739f2dfeb06d358441f60932cf243a7, - address: AztecAddress { inner: 0x178916e52e64b3880e7f837ab30e58222b2971f0150cbc16060bb3589fd96e23 }, - partial_address: PartialAddress { inner: 0x009a6e74b3cebfe0351ceec448d722aa973c1c9da63bbf9f902f1896f3fa1832 }, + address: AztecAddress { inner: 0x1a20abed0eeb77fbcf0dd6ba6e9d9fd18b649277b9aea88014e1e9e39646b1b3 }, + partial_address: PartialAddress { inner: 0x127bbd73a3cf497fb2d85342571695d894985b449a9343eec55485e9cbc514f8 }, portal_contract_address: EthAddress { inner: 0x0000000000000000000000000000000000000913 }, contract_class_id: ContractClassId { inner: 0x1f1f963a350e2c883cc6730c19fc5d5b47a40694d805cbb0720fa76fe295df90 }, public_keys_hash: PublicKeysHash { inner: 0x00000000000000000000000000000000000000000000000000000000000011c1 }, - salted_initialization_hash: SaltedInitializationHash { inner: 0x275e153c147f9cb61a5e7b354e81484696d6a54ffc251dcf4ed8276ab24868d2 } + salted_initialization_hash: SaltedInitializationHash { inner: 0x04643e65513869350552499ed3412df59540dffe3cd698203deee8900b53bcec }, + deployer: AztecAddress { inner: 0x0000000000000000000000000000000000000000000000000000000000000000 } }" `; 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 0322787c46a..8ec8f5f6dbd 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 @@ -1,5 +1,6 @@ import { MerkleTreeId } from '@aztec/circuit-types'; import { + AztecAddress, EthAddress, FunctionSelector, NOTE_HASH_TREE_HEIGHT, @@ -59,7 +60,8 @@ describe('Data generation for noir tests', () => { const initializationHash = computeInitializationHashFromEncodedArgs(constructorSelector, []); const { artifactHash, privateFunctionsRoot, publicBytecodeCommitment } = computeContractClassIdPreimage(contractClass); - const instance: ContractInstance = { ...contract, version: 1, initializationHash, contractClassId }; + const deployer = AztecAddress.ZERO; + const instance: ContractInstance = { ...contract, version: 1, initializationHash, contractClassId, deployer }; const address = computeContractAddressFromInstance(instance); const saltedInitializationHash = computeSaltedInitializationHash(instance); const partialAddress = computePartialAddress(instance); @@ -77,6 +79,7 @@ describe('Data generation for noir tests', () => { contract_class_id: `ContractClassId { inner: ${contractClassId.toString()} }`, public_keys_hash: `PublicKeysHash { inner: ${contract.publicKeysHash.toString()} }`, salted_initialization_hash: `SaltedInitializationHash { inner: ${saltedInitializationHash.toString()} }`, + deployer: `AztecAddress { inner: ${deployer.toString()} }`, }), ).toMatchSnapshot(); /* eslint-enable camelcase */ diff --git a/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap index 1b7e5ec8cc8..5a1ca9ca985 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/class-registerer/__snapshots__/index.test.ts.snap @@ -2,7 +2,7 @@ exports[`ClassRegisterer returns canonical protocol contract 1`] = ` { - "address": AztecAddress<0x1ff58462bc433d2e277ca6371c796fcc2dbfae869edf87b60f7fdff335ae8baa>, + "address": AztecAddress<0x2140db629d95644ef26140fa5ae87749ae28d373176af9a2e458052ced96c7b3>, "contractClass": { "artifactHash": Fr<0x20d64bd232dd14c00fedcaf31d10ad879a63eee4183824d1cadf2dac49b1f9ce>, "id": Fr<0x2ced3a9029e1a272aa799dd8d66c3fae1dffed2259cc96b578a4bcc997d6c7b1>, @@ -30,8 +30,9 @@ exports[`ClassRegisterer returns canonical protocol contract 1`] = ` "version": 1, }, "instance": { - "address": AztecAddress<0x1ff58462bc433d2e277ca6371c796fcc2dbfae869edf87b60f7fdff335ae8baa>, + "address": AztecAddress<0x2140db629d95644ef26140fa5ae87749ae28d373176af9a2e458052ced96c7b3>, "contractClassId": Fr<0x2ced3a9029e1a272aa799dd8d66c3fae1dffed2259cc96b578a4bcc997d6c7b1>, + "deployer": AztecAddress<0x0000000000000000000000000000000000000000000000000000000000000000>, "initializationHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, "portalContractAddress": EthAddress<0x0000000000000000000000000000000000000000>, "publicKeysHash": Fr<0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed>, diff --git a/yarn-project/protocol-contracts/src/class-registerer/index.ts b/yarn-project/protocol-contracts/src/class-registerer/index.ts index 8492aace1e3..bf0884f77d4 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/index.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/index.ts @@ -13,5 +13,5 @@ export function getCanonicalClassRegisterer(): ProtocolContract { * @remarks This should not change often, hence we hardcode it to save from having to recompute it every time. */ export const ClassRegistererAddress = AztecAddress.fromString( - '0x1ff58462bc433d2e277ca6371c796fcc2dbfae869edf87b60f7fdff335ae8baa', + '0x2140db629d95644ef26140fa5ae87749ae28d373176af9a2e458052ced96c7b3', ); diff --git a/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap index 5383e76120e..a4c6a95fb82 100644 --- a/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/gas-token/__snapshots__/index.test.ts.snap @@ -2,10 +2,11 @@ exports[`GasToken returns canonical protocol contract 1`] = ` { - "address": AztecAddress<0x00172847c5512e54f1299e84e95196ac398adcc39068ed534e853ae9b0487e72>, + "address": AztecAddress<0x0fbb005a23dac52e3c328b63834c2402ad4fb90e143cf73750af50f2a1e03646>, "instance": { - "address": AztecAddress<0x00172847c5512e54f1299e84e95196ac398adcc39068ed534e853ae9b0487e72>, + "address": AztecAddress<0x0fbb005a23dac52e3c328b63834c2402ad4fb90e143cf73750af50f2a1e03646>, "contractClassId": Fr<0x210658dedea30a0f6a675e0ad7e58d34b6c092c5248f81515391fbe3e1f38398>, + "deployer": AztecAddress<0x0000000000000000000000000000000000000000000000000000000000000000>, "initializationHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, "portalContractAddress": EthAddress<0x0000000000000000000000000000000000000000>, "publicKeysHash": Fr<0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed>, diff --git a/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap b/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap index 8f30d5d38c4..1550776e587 100644 --- a/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap +++ b/yarn-project/protocol-contracts/src/instance-deployer/__snapshots__/index.test.ts.snap @@ -2,7 +2,7 @@ exports[`InstanceDeployer returns canonical protocol contract 1`] = ` { - "address": AztecAddress<0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3>, + "address": AztecAddress<0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78>, "contractClass": { "artifactHash": Fr<0x088abf2a235b9047f92f51a30630eeec47614f5f997d42aa70bc6c20ecf27b87>, "id": Fr<0x22905afb676c1c6736b344418266b16dbf06c2e13bdcd386617bc5b8db3103df>, @@ -20,8 +20,9 @@ exports[`InstanceDeployer returns canonical protocol contract 1`] = ` "version": 1, }, "instance": { - "address": AztecAddress<0x0bffa876f07f9fe1802579dfef599810202f9c25b9a2f58921064a267d1ad1d3>, + "address": AztecAddress<0x00de4d0d9913ddba5fbba9286031b4a5dc9b2af5e824154ae75938f96c1bfe78>, "contractClassId": Fr<0x22905afb676c1c6736b344418266b16dbf06c2e13bdcd386617bc5b8db3103df>, + "deployer": AztecAddress<0x0000000000000000000000000000000000000000000000000000000000000000>, "initializationHash": Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, "portalContractAddress": EthAddress<0x0000000000000000000000000000000000000000>, "publicKeysHash": Fr<0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed>, diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index 8641bbf0a6d..ee7435b180e 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -22,7 +22,6 @@ import { import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { createDebugLogger } from '@aztec/foundation/log'; import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer'; -import { InstanceDeployerAddress } from '@aztec/protocol-contracts/instance-deployer'; import { CommitmentsDB, MessageLoadOracleInputs, PublicContractsDB, PublicStateDB } from '@aztec/simulator'; import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts'; import { MerkleTreeOperations } from '@aztec/world-state'; @@ -51,7 +50,7 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { this.log(`Adding class ${e.contractClassId.toString()} to public execution contract cache`); this.classCache.set(e.contractClassId.toString(), e.toContractClassPublic()); }); - ContractInstanceDeployedEvent.fromLogs(logs, InstanceDeployerAddress).forEach(e => { + ContractInstanceDeployedEvent.fromLogs(logs).forEach(e => { this.log( `Adding instance ${e.address.toString()} with class ${e.contractClassId.toString()} to public execution contract cache`, ); @@ -73,9 +72,7 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { ContractClassRegisteredEvent.fromLogs(logs, ClassRegistererAddress).forEach(e => this.classCache.delete(e.contractClassId.toString()), ); - ContractInstanceDeployedEvent.fromLogs(logs, InstanceDeployerAddress).forEach(e => - this.instanceCache.delete(e.address.toString()), - ); + ContractInstanceDeployedEvent.fromLogs(logs).forEach(e => this.instanceCache.delete(e.address.toString())); return Promise.resolve(); } diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index f35b3fe1ec1..356691b00ce 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -47,11 +47,10 @@ export class Oracle { async getContractInstance([address]: ACVMField[]) { const instance = await this.typedOracle.getContractInstance(AztecAddress.fromField(fromACVMField(address))); - // TODO(#4434) Add deployer field to ContractInstance - const deployer = Fr.ZERO; + return [ instance.salt, - deployer, + instance.deployer, instance.contractClassId, instance.initializationHash, instance.portalContractAddress, diff --git a/yarn-project/types/src/contracts/contract_instance.ts b/yarn-project/types/src/contracts/contract_instance.ts index 71ac496c076..aa77fbe7192 100644 --- a/yarn-project/types/src/contracts/contract_instance.ts +++ b/yarn-project/types/src/contracts/contract_instance.ts @@ -19,6 +19,8 @@ export interface ContractInstance { portalContractAddress: EthAddress; /** Optional hash of the struct of public keys used for encryption and nullifying by this contract. */ publicKeysHash: Fr; + /** Optional deployer address or zero if this was a universal deploy. */ + deployer: AztecAddress; } export type ContractInstanceWithAddress = ContractInstance & { address: AztecAddress }; @@ -30,6 +32,7 @@ export class SerializableContractInstance { public readonly initializationHash: Fr; public readonly portalContractAddress: EthAddress; public readonly publicKeysHash: Fr; + public readonly deployer: AztecAddress; constructor(instance: ContractInstance) { if (instance.version !== VERSION) { @@ -40,6 +43,7 @@ export class SerializableContractInstance { this.initializationHash = instance.initializationHash; this.portalContractAddress = instance.portalContractAddress; this.publicKeysHash = instance.publicKeysHash; + this.deployer = instance.deployer; } public toBuffer() { @@ -50,6 +54,7 @@ export class SerializableContractInstance { this.initializationHash, this.portalContractAddress, this.publicKeysHash, + this.deployer, ); } @@ -67,6 +72,7 @@ export class SerializableContractInstance { initializationHash: reader.readObject(Fr), portalContractAddress: reader.readObject(EthAddress), publicKeysHash: reader.readObject(Fr), + deployer: reader.readObject(AztecAddress), }); } @@ -78,6 +84,7 @@ export class SerializableContractInstance { initializationHash: Fr.random(), portalContractAddress: EthAddress.random(), publicKeysHash: Fr.random(), + deployer: AztecAddress.random(), }); } }