diff --git a/yarn-project/aztec.js/src/account_manager/index.ts b/yarn-project/aztec.js/src/account_manager/index.ts index d53c85b587a6..7814dafa78fa 100644 --- a/yarn-project/aztec.js/src/account_manager/index.ts +++ b/yarn-project/aztec.js/src/account_manager/index.ts @@ -104,12 +104,10 @@ export class AccountManager { */ public async register(opts: WaitOpts = DefaultWaitOpts): Promise { await this.#register(); - await this.pxe.addContracts([ - { - artifact: this.accountContract.getContractArtifact(), - instance: this.getInstance(), - }, - ]); + await this.pxe.registerContract({ + artifact: this.accountContract.getContractArtifact(), + instance: this.getInstance(), + }); await waitForAccountSynch(this.pxe, this.getCompleteAddress(), opts); return this.getWallet(); diff --git a/yarn-project/aztec.js/src/contract/contract_base.ts b/yarn-project/aztec.js/src/contract/contract_base.ts index d44555f57be3..2767a42e0d1e 100644 --- a/yarn-project/aztec.js/src/contract/contract_base.ts +++ b/yarn-project/aztec.js/src/contract/contract_base.ts @@ -1,4 +1,3 @@ -import { DeployedContract } from '@aztec/circuit-types'; import { computePartialAddress } from '@aztec/circuits.js'; import { ContractArtifact, FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; import { ContractInstanceWithAddress } from '@aztec/types/contracts'; @@ -20,7 +19,7 @@ export type ContractMethod = ((...args: any[]) => ContractFunctionInteraction) & /** * Abstract implementation of a contract extended by the Contract class and generated contract types. */ -export class ContractBase implements DeployedContract { +export class ContractBase { /** * An object containing contract methods mapped to their respective names. */ diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 43a8560b922f..9eaadcb6b0bd 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -92,7 +92,7 @@ export class DeployMethod extends Bas } this.txRequest = await this.wallet.createTxExecutionRequest(await this.request(options)); // TODO: Should we add the contracts to the DB here, or once the tx has been sent or mined? - await this.pxe.addContracts([{ artifact: this.artifact, instance: this.instance! }]); + await this.pxe.registerContract({ artifact: this.artifact, instance: this.instance! }); } return this.txRequest; } diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index 5ba67b2a28fc..3a1867018916 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -82,7 +82,7 @@ export { AztecNode, Body, CompleteAddress, - DeployedContract, + ContractWithArtifact, ExtendedNote, FunctionCall, GrumpkinPrivateKey, diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index f1cfa590f920..e00c9322831e 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -1,6 +1,6 @@ import { AuthWitness, - DeployedContract, + ContractWithArtifact, ExtendedNote, FunctionCall, GetUnencryptedLogsResponse, @@ -16,6 +16,7 @@ import { TxReceipt, } from '@aztec/circuit-types'; import { AztecAddress, CompleteAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js'; +import { ContractArtifact } from '@aztec/foundation/abi'; import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts'; import { NodeInfo } from '@aztec/types/interfaces'; @@ -64,8 +65,11 @@ export abstract class BaseWallet implements Wallet { getRecipient(address: AztecAddress): Promise { return this.pxe.getRecipient(address); } - addContracts(contracts: DeployedContract[]): Promise { - return this.pxe.addContracts(contracts); + registerContract(contract: ContractWithArtifact): Promise { + return this.pxe.registerContract(contract); + } + registerContractClass(artifact: ContractArtifact): Promise { + return this.pxe.registerContractClass(artifact); } getContracts(): Promise { return this.pxe.getContracts(); diff --git a/yarn-project/circuit-types/src/interfaces/contract-with-artifact.ts b/yarn-project/circuit-types/src/interfaces/contract-with-artifact.ts new file mode 100644 index 000000000000..b42bd1b24d5d --- /dev/null +++ b/yarn-project/circuit-types/src/interfaces/contract-with-artifact.ts @@ -0,0 +1,20 @@ +import { ContractArtifact } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; + +/** + * Container for contract instance and artifact or class id. + */ +export type ContractWithArtifact = ( + | { + /** The artifact of the contract. */ + artifact: ContractArtifact; + } + | { + /** The class id of the contract artifact. */ + contractClassId: Fr; + } +) & { + /** The contract instance. */ + instance: ContractInstanceWithAddress; +}; diff --git a/yarn-project/circuit-types/src/interfaces/deployed-contract.ts b/yarn-project/circuit-types/src/interfaces/deployed-contract.ts deleted file mode 100644 index 5bf48600d64a..000000000000 --- a/yarn-project/circuit-types/src/interfaces/deployed-contract.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ContractArtifact } from '@aztec/foundation/abi'; -import { ContractInstanceWithAddress } from '@aztec/types/contracts'; - -/** - * Represents a deployed contract on the Aztec network. - * Contains the contract artifact, address, and associated portal contract address. - */ -export interface DeployedContract { - /** - * The artifact of the deployed contract. - */ - artifact: ContractArtifact; - /** - * The contract instance. - */ - instance: ContractInstanceWithAddress; -} diff --git a/yarn-project/circuit-types/src/interfaces/index.ts b/yarn-project/circuit-types/src/interfaces/index.ts index f12dff9dfcdf..0e3803aae782 100644 --- a/yarn-project/circuit-types/src/interfaces/index.ts +++ b/yarn-project/circuit-types/src/interfaces/index.ts @@ -1,6 +1,6 @@ export * from './aztec-node.js'; export * from './pxe.js'; -export * from './deployed-contract.js'; +export * from './contract-with-artifact.js'; export * from './sync-status.js'; export * from './configs.js'; export * from './nullifier_tree.js'; diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index 38b7b36e9180..1ca14a130d7f 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -1,4 +1,5 @@ import { AztecAddress, CompleteAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js'; +import { ContractArtifact } from '@aztec/foundation/abi'; import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts'; import { NodeInfo } from '@aztec/types/interfaces'; @@ -10,7 +11,7 @@ import { NoteFilter } from '../notes/note_filter.js'; import { Tx, TxHash, TxReceipt } from '../tx/index.js'; import { TxEffect } from '../tx_effect.js'; import { TxExecutionRequest } from '../tx_execution_request.js'; -import { DeployedContract } from './deployed-contract.js'; +import { ContractWithArtifact } from './contract-with-artifact.js'; import { SyncStatus } from './sync-status.js'; // docs:start:pxe-interface @@ -98,15 +99,22 @@ export interface PXE { */ getRecipient(address: AztecAddress): Promise; + /** + * Registers a contract class in the PXE without registering any associated contract instance with it. + * + * @param artifact - The build artifact for the contract class. + */ + registerContractClass(artifact: ContractArtifact): Promise; + /** * Adds deployed contracts to the PXE Service. Deployed contract information is used to access the * contract code when simulating local transactions. This is automatically called by aztec.js when * deploying a contract. Dapps that wish to interact with contracts already deployed should register * these contracts in their users' PXE Service through this method. * - * @param contracts - An array of DeployedContract objects containing contract ABI, address, and portal contract. + * @param contract - An object containing contract artifact and instance. */ - addContracts(contracts: DeployedContract[]): Promise; + registerContract(contract: ContractWithArtifact): Promise; /** * Retrieves the addresses of contracts added to this PXE Service. diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 743a0809168b..71bcae25a4ae 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -1,7 +1,6 @@ import { AztecAddress, CallRequest, - Fr, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, Proof, @@ -10,10 +9,11 @@ import { ContractArtifact } from '@aztec/foundation/abi'; import { makeTuple } from '@aztec/foundation/array'; import { times } from '@aztec/foundation/collection'; import { randomBytes } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; import { Tuple } from '@aztec/foundation/serialize'; import { ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; -import { DeployedContract } from './interfaces/index.js'; +import { ContractWithArtifact } from './interfaces/index.js'; import { FunctionL2Logs, Note, TxL2Logs } from './logs/index.js'; import { makePrivateKernelTailCircuitPublicInputs, makePublicCallRequest } from './mocks_to_purge.js'; import { ExtendedNote } from './notes/index.js'; @@ -59,10 +59,10 @@ export const randomContractArtifact = (): ContractArtifact => ({ fileMap: {}, }); -export const randomContractInstanceWithAddress = (): ContractInstanceWithAddress => - SerializableContractInstance.random().withAddress(AztecAddress.random()); +export const randomContractInstanceWithAddress = (opts: { contractClassId?: Fr } = {}): ContractInstanceWithAddress => + SerializableContractInstance.random(opts).withAddress(AztecAddress.random()); -export const randomDeployedContract = (): DeployedContract => ({ +export const randomDeployedContract = (): ContractWithArtifact => ({ artifact: randomContractArtifact(), instance: randomContractInstanceWithAddress(), }); diff --git a/yarn-project/cli/src/cmds/add_contract.ts b/yarn-project/cli/src/cmds/add_contract.ts index 34fb98172905..b09e87d6eb7f 100644 --- a/yarn-project/cli/src/cmds/add_contract.ts +++ b/yarn-project/cli/src/cmds/add_contract.ts @@ -42,6 +42,6 @@ export async function addContract( const client = await createCompatibleClient(rpcUrl, debugLogger); - await client.addContracts([{ artifact, instance }]); + await client.registerContract({ artifact, instance }); log(`\nContract added to PXE at ${address.toString()} with class ${instance.contractClassId.toString()}\n`); } diff --git a/yarn-project/end-to-end/src/benchmarks/utils.ts b/yarn-project/end-to-end/src/benchmarks/utils.ts index 6d567e12b36a..8fe85f16166a 100644 --- a/yarn-project/end-to-end/src/benchmarks/utils.ts +++ b/yarn-project/end-to-end/src/benchmarks/utils.ts @@ -111,7 +111,7 @@ export async function waitNewPXESynced( startingBlock: number = INITIAL_L2_BLOCK_NUM, ): Promise { const pxe = await createPXEService(node, { l2BlockPollingIntervalMS: 100, l2StartingBlock: startingBlock }); - await pxe.addContracts([contract]); + await pxe.registerContract(contract); await retryUntil(() => pxe.isGlobalStateSynchronized(), 'pxe-global-sync'); return pxe; } diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index 5f34262853a3..06e3a3f4f44a 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -130,12 +130,10 @@ describe('e2e_2_pxes', () => { await pxeB.registerRecipient(userA); // Add token to PXE B (PXE A already has it because it was deployed through it) - await pxeB.addContracts([ - { - artifact: TokenContract.artifact, - instance: tokenInstance, - }, - ]); + await pxeB.registerContract({ + artifact: TokenContract.artifact, + instance: tokenInstance, + }); // Check initial balances and logs are as expected await expectTokenBalance(walletA, tokenAddress, userA.address, initialBalance); @@ -188,12 +186,10 @@ describe('e2e_2_pxes', () => { await awaitServerSynchronized(pxeA); // Add Child to PXE B - await pxeB.addContracts([ - { - artifact: ChildContract.artifact, - instance: childCompleteAddress, - }, - ]); + await pxeB.registerContract({ + artifact: ChildContract.artifact, + instance: childCompleteAddress, + }); const newValueToSet = new Fr(256n); @@ -222,12 +218,10 @@ describe('e2e_2_pxes', () => { await pxeB.registerRecipient(userA); // Add token to PXE B (PXE A already has it because it was deployed through it) - await pxeB.addContracts([ - { - artifact: TokenContract.artifact, - instance: tokenInstance, - }, - ]); + await pxeB.registerContract({ + artifact: TokenContract.artifact, + instance: tokenInstance, + }); // Mint tokens to user B await mintTokens(contractWithWalletA, userB.address, userBBalance, pxeA); @@ -287,12 +281,10 @@ describe('e2e_2_pxes', () => { await contractWithWalletA.methods.transfer(userA.address, userB.address, transferAmount1, 0).send().wait(); // now add the contract and check balances - await pxeB.addContracts([ - { - artifact: TokenContract.artifact, - instance: tokenInstance, - }, - ]); + await pxeB.registerContract({ + artifact: TokenContract.artifact, + instance: tokenInstance, + }); await expectTokenBalance(walletA, tokenAddress, userA.address, initialBalance - transferAmount1); await expectTokenBalance(walletB, tokenAddress, userB.address, transferAmount1); }); @@ -347,12 +339,10 @@ describe('e2e_2_pxes', () => { // PXE-B had previously deferred the notes from A -> Shared, and Shared -> B // PXE-B adds the contract // PXE-B reprocesses the deferred notes, and sees the nullifier for A -> Shared - await pxeB.addContracts([ - { - artifact: TokenContract.artifact, - instance: tokenInstance, - }, - ]); + await pxeB.registerContract({ + artifact: TokenContract.artifact, + instance: tokenInstance, + }); await expectTokenBalance(walletB, tokenAddress, userB.address, transferAmount2); await expect(sharedWalletOnB.isAccountStateSynchronized(sharedAccountAddress.address)).resolves.toBe(true); await expectTokenBalance( 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 835be3a99378..3a05afc5ad2d 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 @@ -454,7 +454,7 @@ describe('e2e_deploy_contract', () => { testDeployingAnInstance('from a contract', async instance => { // Register the instance to be deployed in the pxe - await wallet.addContracts([{ artifact, instance }]); + await wallet.registerContract({ artifact, instance }); // Set up the contract that calls the deployer (which happens to be the TestContract) and call it const deployer = await TestContract.deploy(wallet).send().deployed(); await deployer.methods.deploy_contract(instance.address).send().wait(); @@ -594,6 +594,6 @@ async function registerContract( portalAddress, deployer, }); - await wallet.addContracts([{ artifact: contractArtifact.artifact, instance }]); + await wallet.registerContract({ artifact: contractArtifact.artifact, instance }); return contractArtifact.at(instance.address, wallet); } diff --git a/yarn-project/end-to-end/src/e2e_persistence.test.ts b/yarn-project/end-to-end/src/e2e_persistence.test.ts index 8f00ac3d9fff..67068f5779db 100644 --- a/yarn-project/end-to-end/src/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/e2e_persistence.test.ts @@ -202,12 +202,10 @@ describe('Aztec persistence', () => { }); it("pxe does not have owner's private notes", async () => { - await context.pxe.addContracts([ - { - artifact: TokenContract.artifact, - instance: contractInstance, - }, - ]); + await context.pxe.registerContract({ + artifact: TokenContract.artifact, + instance: contractInstance, + }); await context.pxe.registerRecipient(ownerAddress); const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitSetup(); @@ -216,12 +214,10 @@ describe('Aztec persistence', () => { }); it('has access to public storage', async () => { - await context.pxe.addContracts([ - { - artifact: TokenContract.artifact, - instance: contractInstance, - }, - ]); + await context.pxe.registerContract({ + artifact: TokenContract.artifact, + instance: contractInstance, + }); const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitSetup(); const contract = await TokenContract.at(contractAddress, wallet); @@ -230,12 +226,10 @@ describe('Aztec persistence', () => { }); it('pxe restores notes after registering the owner', async () => { - await context.pxe.addContracts([ - { - artifact: TokenContract.artifact, - instance: contractInstance, - }, - ]); + await context.pxe.registerContract({ + artifact: TokenContract.artifact, + instance: contractInstance, + }); const ownerAccount = getUnsafeSchnorrAccount(context.pxe, ownerPrivateKey, ownerSalt); await ownerAccount.register(); @@ -263,12 +257,10 @@ describe('Aztec persistence', () => { beforeAll(async () => { const temporaryContext = await setup(0, { deployL1ContractsValues }, {}); - await temporaryContext.pxe.addContracts([ - { - artifact: TokenContract.artifact, - instance: contractInstance, - }, - ]); + await temporaryContext.pxe.registerContract({ + artifact: TokenContract.artifact, + instance: contractInstance, + }); const ownerAccount = getUnsafeSchnorrAccount(temporaryContext.pxe, ownerPrivateKey, ownerSalt); await ownerAccount.register(); diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 766c4dcaa2ad..780d5cd721f0 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -38,7 +38,8 @@ "./committable": "./dest/committable/index.js", "./noir": "./dest/noir/index.js", "./testing": "./dest/testing/index.js", - "./array": "./dest/array/index.js" + "./array": "./dest/array/index.js", + "./validation": "./dest/validation/index.js" }, "scripts": { "build": "yarn clean && tsc -b", diff --git a/yarn-project/foundation/src/validation/index.ts b/yarn-project/foundation/src/validation/index.ts new file mode 100644 index 000000000000..f8b4be387821 --- /dev/null +++ b/yarn-project/foundation/src/validation/index.ts @@ -0,0 +1,7 @@ +/** Utility function to throw an error if a required value is missing. */ +export function required(value: T | undefined, errMsg?: string): T { + if (value === undefined) { + throw new Error(errMsg || 'Value is required'); + } + return value; +} diff --git a/yarn-project/pxe/src/pxe_service/create_pxe_service.ts b/yarn-project/pxe/src/pxe_service/create_pxe_service.ts index a8c92374eadc..98c9c1809c9b 100644 --- a/yarn-project/pxe/src/pxe_service/create_pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/create_pxe_service.ts @@ -43,7 +43,9 @@ export async function createPXEService( const db = new KVPxeDatabase(await initStoreForRollup(AztecLmdbStore.open(pxeDbPath), l1Contracts.rollupAddress)); const server = new PXEService(keyStore, aztecNode, db, config, logSuffix); - await server.addContracts([getCanonicalClassRegisterer(), getCanonicalInstanceDeployer(), getCanonicalGasToken()]); + for (const contract of [getCanonicalClassRegisterer(), getCanonicalInstanceDeployer(), getCanonicalGasToken()]) { + await server.registerContract(contract); + } await server.start(); return server; diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 77ff66cf11a8..769f7721272e 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -1,7 +1,7 @@ import { AuthWitness, AztecNode, - DeployedContract, + ContractWithArtifact, ExtendedNote, FunctionCall, GetUnencryptedLogsResponse, @@ -37,12 +37,13 @@ import { getContractClassFromArtifact, } from '@aztec/circuits.js'; import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/hash'; -import { DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; +import { ContractArtifact, DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { SerialQueue } from '@aztec/foundation/fifo'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; +import { required } from '@aztec/foundation/validation'; import { AcirSimulator, ExecutionResult, @@ -215,18 +216,29 @@ export class PXEService implements PXE { return Promise.resolve(recipient); } - public async addContracts(contracts: DeployedContract[]) { - for (const contract of contracts) { - const { instance, artifact } = contract; - const artifactHash = computeArtifactHash(artifact); - const contractClassId = computeContractClassId(getContractClassFromArtifact({ ...artifact, artifactHash })); - await this.db.addContractArtifact(contractClassId, artifact); - await this.db.addContractInstance(instance); - const hasPortal = instance.portalContractAddress && !instance.portalContractAddress.isZero(); - const portalInfo = hasPortal ? ` with portal ${instance.portalContractAddress.toChecksumString()}` : ''; - this.log.info(`Added contract ${artifact.name} at ${instance.address.toString()}${portalInfo}`); - await this.synchronizer.reprocessDeferredNotesForContract(instance.address); - } + public async registerContractClass(artifact: ContractArtifact): Promise { + const contractClassId = computeContractClassId(getContractClassFromArtifact(artifact)); + await this.db.addContractArtifact(contractClassId, artifact); + this.log.info(`Added contract class ${artifact.name} with id ${contractClassId}`); + } + + public async registerContract(contract: ContractWithArtifact) { + const instance = contract.instance; + const artifact = + 'artifact' in contract + ? contract.artifact + : required( + await this.db.getContractArtifact(contract.contractClassId), + `Unknown artifact for class id ${contract.contractClassId} when registering ${instance.address}`, + ); + const artifactHash = computeArtifactHash(artifact); + const contractClassId = computeContractClassId(getContractClassFromArtifact({ ...artifact, artifactHash })); + await this.db.addContractArtifact(contractClassId, artifact); + await this.db.addContractInstance(instance); + const hasPortal = instance.portalContractAddress && !instance.portalContractAddress.isZero(); + const portalInfo = hasPortal ? ` with portal ${instance.portalContractAddress.toChecksumString()}` : ''; + this.log.info(`Added contract ${artifact.name} at ${instance.address.toString()}${portalInfo}`); + await this.synchronizer.reprocessDeferredNotesForContract(instance.address); } public getContracts(): Promise { diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts index 2b6cb8c96651..6772bced9a10 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts @@ -1,4 +1,11 @@ -import { DeployedContract, PXE, TxExecutionRequest, randomDeployedContract } from '@aztec/circuit-types'; +import { + ContractWithArtifact, + PXE, + TxExecutionRequest, + randomContractArtifact, + randomContractInstanceWithAddress, + randomDeployedContract, +} from '@aztec/circuit-types'; import { AztecAddress, CompleteAddress, @@ -7,6 +14,7 @@ import { INITIAL_L2_BLOCK_NUM, Point, TxContext, + getContractClassFromArtifact, } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { ConstantKeyPair } from '@aztec/key-store'; @@ -82,16 +90,36 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise) => }); it('successfully adds a contract', async () => { - const contracts: DeployedContract[] = [randomDeployedContract(), randomDeployedContract()]; - await pxe.addContracts(contracts); + const contracts: ContractWithArtifact[] = [randomDeployedContract(), randomDeployedContract()]; + for (const contract of contracts) { + await pxe.registerContract(contract); + } const expectedContractAddresses = contracts.map(contract => contract.instance.address); const contractAddresses = await pxe.getContracts(); - - // check if all the contracts were returned expect(contractAddresses).toEqual(expect.arrayContaining(expectedContractAddresses)); }); + it('registers a class and adds a contract for it', async () => { + const artifact = randomContractArtifact(); + const contractClass = getContractClassFromArtifact(artifact); + const contractClassId = contractClass.id; + const instance = randomContractInstanceWithAddress({ contractClassId }); + + await pxe.registerContractClass(artifact); + expect(await pxe.getContractClass(contractClassId)).toEqual(contractClass); + + await pxe.registerContract({ contractClassId, instance }); + expect(await pxe.getContractInstance(instance.address)).toEqual(instance); + }); + + it('refuses to register a contract with a class that has not been registered', async () => { + const instance = randomContractInstanceWithAddress(); + await expect(pxe.registerContract({ contractClassId: Fr.random(), instance })).rejects.toThrow( + /Unknown artifact/i, + ); + }); + it('throws when simulating a tx targeting public entrypoint', async () => { const functionData = FunctionData.empty(); functionData.isPrivate = false; diff --git a/yarn-project/types/src/contracts/contract_instance.ts b/yarn-project/types/src/contracts/contract_instance.ts index aa77fbe71929..354857b6b618 100644 --- a/yarn-project/types/src/contracts/contract_instance.ts +++ b/yarn-project/types/src/contracts/contract_instance.ts @@ -2,6 +2,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize'; +import { FieldsOf } from '@aztec/foundation/types'; const VERSION = 1 as const; @@ -76,7 +77,7 @@ export class SerializableContractInstance { }); } - static random() { + static random(opts: Partial> = {}) { return new SerializableContractInstance({ version: VERSION, salt: Fr.random(), @@ -85,6 +86,7 @@ export class SerializableContractInstance { portalContractAddress: EthAddress.random(), publicKeysHash: Fr.random(), deployer: AztecAddress.random(), + ...opts, }); } }