From ff7ad3030cc786ceb8525fec488555d42343a02f Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 8 Aug 2023 12:31:23 -0300 Subject: [PATCH] chore(tests): Use account class for e2e browser tests (#1446) Use the `Account` class introduced in #1429 for the e2e browser tests. This checks that the ABIs of the account contracts are properly packaged by webpack. Also removes the usage of custom utils for creating and retrieving wallets (we just need to remove them from the CLI now). --- yarn-project/aztec.js/src/account/account.ts | 13 +-- yarn-project/aztec.js/src/account/index.ts | 36 +++++++- .../aztec.js/src/aztec_rpc_client/wallet.ts | 19 ++++- yarn-project/aztec.js/src/utils/account.ts | 6 +- .../src/e2e_aztec_js_browser.test.ts | 85 +++++++++---------- yarn-project/end-to-end/src/fixtures/utils.ts | 4 +- 6 files changed, 102 insertions(+), 61 deletions(-) diff --git a/yarn-project/aztec.js/src/account/account.ts b/yarn-project/aztec.js/src/account/account.ts index 823ea801dea..60b1174bf41 100644 --- a/yarn-project/aztec.js/src/account/account.ts +++ b/yarn-project/aztec.js/src/account/account.ts @@ -1,7 +1,7 @@ import { Fr, PublicKey, getContractDeploymentInfo } from '@aztec/circuits.js'; import { AztecRPC, PrivateKey } from '@aztec/types'; -import { AccountWallet, ContractDeployer, DeployMethod, WaitOpts, Wallet, generatePublicKey } from '../index.js'; +import { AccountWallet, ContractDeployer, DeployMethod, WaitOpts, generatePublicKey } from '../index.js'; import { CompleteAddress, isCompleteAddress } from './complete_address.js'; import { DeployAccountSentTx } from './deploy_account_sent_tx.js'; import { AccountContract, Entrypoint, Salt } from './index.js'; @@ -71,9 +71,9 @@ export class Account { * instances to be interacted with from this account. * @returns A Wallet instance. */ - public async getWallet(): Promise { + public async getWallet(): Promise { const entrypoint = await this.getEntrypoint(); - return new AccountWallet(this.rpc, entrypoint); + return new AccountWallet(this.rpc, entrypoint, await this.getCompleteAddress()); } /** @@ -82,7 +82,7 @@ export class Account { * Use the returned wallet to create Contract instances to be interacted with from this account. * @returns A Wallet instance. */ - public async register(): Promise { + public async register(): Promise { const { address, partialAddress } = await this.getCompleteAddress(); await this.rpc.addAccount(this.encryptionPrivateKey, address, partialAddress); return this.getWallet(); @@ -130,7 +130,8 @@ export class Account { * @param opts - Options to wait for the tx to be mined. * @returns A Wallet instance. */ - public async waitDeploy(opts: WaitOpts = {}): Promise { - return (await this.deploy()).getWallet(opts); + public async waitDeploy(opts: WaitOpts = {}): Promise { + await this.deploy().then(tx => tx.wait(opts)); + return this.getWallet(); } } diff --git a/yarn-project/aztec.js/src/account/index.ts b/yarn-project/aztec.js/src/account/index.ts index 0d81f73c575..666dd7249ac 100644 --- a/yarn-project/aztec.js/src/account/index.ts +++ b/yarn-project/aztec.js/src/account/index.ts @@ -1,6 +1,6 @@ import { AztecRPC, PrivateKey } from '@aztec/types'; -import { Fr } from '../index.js'; +import { AccountContract, AccountWallet, AztecAddress, Fr } from '../index.js'; import { Account } from './account.js'; import { CompleteAddress } from './complete_address.js'; import { EcdsaAccountContract } from './contract/ecdsa_account_contract.js'; @@ -65,3 +65,37 @@ export function getUnsafeSchnorrAccount( saltOrAddress, ); } + +/** + * Gets a wallet for an already registered account using Schnorr signatures with a single key for encryption and authentication. + * @param rpc - An AztecRPC server instance. + * @param address - Address for the account. + * @param signingPrivateKey - Grumpkin key used for note encryption and signing transactions. + * @returns A wallet for this account that can be used to interact with a contract instance. + */ +export function getUnsafeSchnorrWallet( + rpc: AztecRPC, + address: AztecAddress, + signingKey: PrivateKey, +): Promise { + return getWallet(rpc, address, new SingleKeyAccountContract(signingKey)); +} + +/** + * Gets a wallet for an already registered account. + * @param rpc - An AztecRPC server instance. + * @param address - Address for the account. + * @param accountContract - Account contract implementation. + * * @returns A wallet for this account that can be used to interact with a contract instance. + */ +export async function getWallet( + rpc: AztecRPC, + address: AztecAddress, + accountContract: AccountContract, +): Promise { + const [publicKey, partialAddress] = await rpc.getPublicKeyAndPartialAddress(address); + const nodeInfo = await rpc.getNodeInfo(); + const completeAddress: CompleteAddress = { publicKey, partialAddress, address }; + const entrypoint = await accountContract.getEntrypoint(completeAddress, nodeInfo); + return new AccountWallet(rpc, entrypoint, completeAddress); +} diff --git a/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts b/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts index a892613850d..b3f32b7a78b 100644 --- a/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts +++ b/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts @@ -15,6 +15,7 @@ import { } from '@aztec/types'; import { CreateTxRequestOpts, Entrypoint } from '../account/entrypoint/index.js'; +import { CompleteAddress } from '../index.js'; /** * The wallet interface. @@ -99,9 +100,9 @@ export abstract class BaseWallet implements Wallet { } /** - * A simple wallet implementation that forwards authentication requests to a provided account implementation. + * A simple wallet implementation that forwards authentication requests to a provided entrypoint implementation. */ -export class AccountWallet extends BaseWallet { +export class EntrypointWallet extends BaseWallet { constructor(rpc: AztecRPC, protected accountImpl: Entrypoint) { super(rpc); } @@ -109,3 +110,17 @@ export class AccountWallet extends BaseWallet { return this.accountImpl.createTxExecutionRequest(executions, opts); } } + +/** + * A wallet implementation that forwards authentication requests to a provided account. + */ +export class AccountWallet extends EntrypointWallet { + constructor(rpc: AztecRPC, protected accountImpl: Entrypoint, protected address: CompleteAddress) { + super(rpc, accountImpl); + } + + /** Returns the complete address of the account that implements this wallet. */ + public getCompleteAddress() { + return this.address; + } +} diff --git a/yarn-project/aztec.js/src/utils/account.ts b/yarn-project/aztec.js/src/utils/account.ts index 0acd2103271..0253d95ca62 100644 --- a/yarn-project/aztec.js/src/utils/account.ts +++ b/yarn-project/aztec.js/src/utils/account.ts @@ -5,7 +5,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { AztecRPC, TxStatus } from '@aztec/types'; import { SingleKeyAccountEntrypoint } from '../account/entrypoint/single_key_account_entrypoint.js'; -import { AccountWallet, Wallet } from '../aztec_rpc_client/wallet.js'; +import { EntrypointWallet, Wallet } from '../aztec_rpc_client/wallet.js'; import { ContractDeployer, EntrypointCollection, generatePublicKey } from '../index.js'; /** @@ -47,7 +47,7 @@ export async function createAccounts( new SingleKeyAccountEntrypoint(address, deploymentInfo.partialAddress, privKey, await Schnorr.new()), ); } - return new AccountWallet(aztecRpcClient, accountImpls); + return new EntrypointWallet(aztecRpcClient, accountImpls); } /** @@ -71,5 +71,5 @@ export async function getAccountWallet( address, new SingleKeyAccountEntrypoint(address, deploymentInfo.partialAddress, privateKey, await Schnorr.new()), ); - return new AccountWallet(aztecRpcClient, accountCollection); + return new EntrypointWallet(aztecRpcClient, accountCollection); } diff --git a/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts index bbd44ab2621..f8d5e409e1c 100644 --- a/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts @@ -1,7 +1,8 @@ +/* eslint-disable no-console */ import * as AztecJs from '@aztec/aztec.js'; import { AztecAddress, PrivateKey } from '@aztec/circuits.js'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { PrivateTokenContractAbi, SchnorrSingleKeyAccountContractAbi } from '@aztec/noir-contracts/artifacts'; +import { PrivateTokenContractAbi } from '@aztec/noir-contracts/artifacts'; import { Server } from 'http'; import Koa from 'koa'; @@ -33,6 +34,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { let contractAddress: AztecAddress; let logger: DebugLogger; + let pageLogger: DebugLogger; let app: Koa; let testClient: AztecJs.AztecRPC; let server: Server; @@ -50,6 +52,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }); logger = createDebugLogger('aztec:aztec.js:web'); + pageLogger = createDebugLogger('aztec:aztec.js:web:page'); browser = await launch({ executablePath: process.env.CHROME_BIN, @@ -68,10 +71,10 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }); page = await browser.newPage(); page.on('console', msg => { - logger('PAGE MSG:', msg.text()); + pageLogger(msg.text()); }); page.on('pageerror', err => { - logger('PAGE ERROR:', err.toString()); + pageLogger.error(err.toString()); }); await page.goto(`http://localhost:${PORT}/index.html`); }); @@ -88,21 +91,20 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }); expect(createAccountsExists).toBe(true); }); + it('Creates an account', async () => { const result = await page.evaluate( - async (rpcUrl, privateKey, schnorrAbi) => { - const { Fr, PrivateKey, createAztecRpcClient, createAccounts, mustSucceedFetch } = window.AztecJs; + async (rpcUrl, privateKeyString) => { + const { PrivateKey, createAztecRpcClient, mustSucceedFetch, getUnsafeSchnorrAccount } = window.AztecJs; const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); - - await createAccounts(client, schnorrAbi, PrivateKey.fromString(privateKey)!, Fr.ZERO); + const privateKey = PrivateKey.fromString(privateKeyString); + await getUnsafeSchnorrAccount(client, privateKey).waitDeploy(); const accounts = await client.getAccounts(); - // eslint-disable-next-line no-console console.log(`Created Account: ${accounts[0].toString()}`); return accounts[0].toString(); }, SANDBOX_URL, privKey.toString(), - SchnorrSingleKeyAccountContractAbi, ); const account = (await testClient.getAccounts())[0]; expect(result).toEqual(account.toString()); @@ -115,11 +117,10 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); const owner = (await client.getAccounts())[0]; const publicKey = await client.getPublicKey(owner); - const tx = new DeployMethod(publicKey, client, PrivateTokenContractAbi, [33n, owner]).send(); - await tx.isMined(); - // eslint-disable-next-line no-console - console.log('Contract Deployed'); + const tx = new DeployMethod(publicKey, client, PrivateTokenContractAbi, [initialBalance, owner]).send(); + await tx.wait(); const receipt = await tx.getReceipt(); + console.log(`Contract Deployed: ${receipt.contractAddress}`); return receipt.txHash.toString(); }, SANDBOX_URL, @@ -134,17 +135,13 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { it("Gets the owner's balance", async () => { const result = await page.evaluate( - async (rpcUrl, privateKey, SchnorrSingleKeyAccountContractAbi, contractAddress, PrivateTokenContractAbi) => { - const { Contract, AztecAddress, Fr, PrivateKey, createAztecRpcClient, getAccountWallet, mustSucceedFetch } = + async (rpcUrl, privateKeyString, contractAddress, PrivateTokenContractAbi) => { + const { Contract, AztecAddress, PrivateKey, createAztecRpcClient, getUnsafeSchnorrWallet, mustSucceedFetch } = window.AztecJs; + const privateKey = PrivateKey.fromString(privateKeyString); const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); - const owner = (await client.getAccounts())[0]; - const wallet = await getAccountWallet( - client, - SchnorrSingleKeyAccountContractAbi, - PrivateKey.fromString(privateKey!), - Fr.ZERO, - ); + const [owner] = await client.getAccounts(); + const wallet = await getUnsafeSchnorrWallet(client, owner, privateKey); const contract = await Contract.create( AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, @@ -155,7 +152,6 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }, SANDBOX_URL, privKey.toString(), - SchnorrSingleKeyAccountContractAbi, contractAddress.toString(), PrivateTokenContractAbi, ); @@ -165,35 +161,31 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { it('Sends a transfer TX', async () => { const result = await page.evaluate( - async ( - rpcUrl, - privateKey, - contractAddress, - transferAmount, - PrivateTokenContractAbi, - SchnorrSingleKeyAccountContractAbi, - ) => { - const { AztecAddress, Contract, Fr, PrivateKey, createAztecRpcClient, getAccountWallet, mustSucceedFetch } = - window.AztecJs; + async (rpcUrl, privateKeyString, contractAddress, transferAmount, PrivateTokenContractAbi) => { + console.log(`Starting transfer tx`); + const { + AztecAddress, + Contract, + PrivateKey, + createAztecRpcClient, + getUnsafeSchnorrAccount, + getUnsafeSchnorrWallet, + mustSucceedFetch, + } = window.AztecJs; const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); - await AztecJs.createAccounts(client, SchnorrSingleKeyAccountContractAbi, PrivateKey.random(), Fr.random()); - const [owner, receiver] = await client.getAccounts(); - // eslint-disable-next-line no-console + const privateKey = PrivateKey.fromString(privateKeyString); + const { address: receiver } = await getUnsafeSchnorrAccount(client, PrivateKey.random()) + .register() + .then(w => w.getCompleteAddress()); console.log(`Created 2nd Account: ${receiver.toString()}`); - const wallet = await getAccountWallet( - client, - SchnorrSingleKeyAccountContractAbi, - PrivateKey.fromString(privateKey!), - Fr.ZERO, - ); + const [owner] = await client.getAccounts(); + const wallet = await getUnsafeSchnorrWallet(client, owner, privateKey); const contract = await Contract.create( AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet, ); - const tx = contract.methods.transfer(transferAmount, owner, receiver).send({ origin: owner }); - await tx.isMined(); - // eslint-disable-next-line no-console + await contract.methods.transfer(transferAmount, owner, receiver).send({ origin: owner }).wait(); console.log(`Transfered ${transferAmount} tokens to new Account`); const [balance] = await contract.methods.getBalance(receiver).view({ from: receiver }); return balance; @@ -203,8 +195,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { contractAddress.toString(), transferAmount, PrivateTokenContractAbi, - SchnorrSingleKeyAccountContractAbi, ); expect(result).toEqual(transferAmount); - }, 45_000); + }, 60_000); }); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 0837e1764fa..0a003030f9f 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -1,12 +1,12 @@ import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { RpcServerConfig, createAztecRPCServer, getConfigEnvVars as getRpcConfigEnvVars } from '@aztec/aztec-rpc'; import { - AccountWallet, Account as AztecAccount, AztecAddress, Contract, ContractDeployer, EntrypointCollection, + EntrypointWallet, EthAddress, Wallet, createAztecRpcClient as createJsonRpcClient, @@ -168,7 +168,7 @@ export async function setupAztecRPCServer( await Promise.all(txs.map(tx => tx.wait({ interval: 0.1 }))); // Assemble them into a single wallet - const wallet = new AccountWallet(aztecRpcServer, await EntrypointCollection.fromAccounts(accounts)); + const wallet = new EntrypointWallet(aztecRpcServer, await EntrypointCollection.fromAccounts(accounts)); return { aztecRpcServer: aztecRpcServer!,