Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(tests): Use account class for e2e browser tests #1446

Merged
merged 3 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions yarn-project/aztec.js/src/account/account.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -71,9 +71,9 @@ export class Account {
* instances to be interacted with from this account.
* @returns A Wallet instance.
*/
public async getWallet(): Promise<Wallet> {
public async getWallet(): Promise<AccountWallet> {
const entrypoint = await this.getEntrypoint();
return new AccountWallet(this.rpc, entrypoint);
return new AccountWallet(this.rpc, entrypoint, await this.getCompleteAddress());
}

/**
Expand All @@ -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<Wallet> {
public async register(): Promise<AccountWallet> {
const { address, partialAddress } = await this.getCompleteAddress();
await this.rpc.addAccount(this.encryptionPrivateKey, address, partialAddress);
return this.getWallet();
Expand Down Expand Up @@ -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<Wallet> {
return (await this.deploy()).getWallet(opts);
public async waitDeploy(opts: WaitOpts = {}): Promise<AccountWallet> {
await this.deploy().then(tx => tx.wait(opts));
return this.getWallet();
}
}
36 changes: 35 additions & 1 deletion yarn-project/aztec.js/src/account/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<AccountWallet> {
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<AccountWallet> {
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);
}
19 changes: 17 additions & 2 deletions yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '@aztec/types';

import { CreateTxRequestOpts, Entrypoint } from '../account/entrypoint/index.js';
import { CompleteAddress } from '../index.js';

/**
* The wallet interface.
Expand Down Expand Up @@ -99,13 +100,27 @@ 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);
}
createTxExecutionRequest(executions: FunctionCall[], opts: CreateTxRequestOpts = {}): Promise<TxExecutionRequest> {
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;
}
}
6 changes: 3 additions & 3 deletions yarn-project/aztec.js/src/utils/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

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

/**
Expand All @@ -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);
}
85 changes: 38 additions & 47 deletions yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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`);
});
Expand All @@ -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());
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -155,7 +152,6 @@ conditionalDescribe()('e2e_aztec.js_browser', () => {
},
SANDBOX_URL,
privKey.toString(),
SchnorrSingleKeyAccountContractAbi,
contractAddress.toString(),
PrivateTokenContractAbi,
);
Expand All @@ -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;
Expand All @@ -203,8 +195,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => {
contractAddress.toString(),
transferAmount,
PrivateTokenContractAbi,
SchnorrSingleKeyAccountContractAbi,
);
expect(result).toEqual(transferAmount);
}, 45_000);
}, 60_000);
});
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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!,
Expand Down