Skip to content

Commit

Permalink
Fixes + updates for Aztec CLI (#1014)
Browse files Browse the repository at this point in the history
* WIP refactor CLI around wallet implementation

* Fix problems with Sandbox example

* updated CLI, working with new Wallet implementation + improved docs

* fix typo

* define privateKey as string in create-account

* bug fixes, deploy args as argument

* check if account already exists

* remove old import

* PR Fixes

* fix bug with account collection

* add env vars

* add public key env var
  • Loading branch information
spypsy authored Jul 12, 2023
1 parent 5d9e254 commit 8cf3be5
Show file tree
Hide file tree
Showing 26 changed files with 603 additions and 275 deletions.
286 changes: 237 additions & 49 deletions yarn-project/aztec-cli/README.md

Large diffs are not rendered by default.

192 changes: 128 additions & 64 deletions yarn-project/aztec-cli/src/index.ts

Large diffs are not rendered by default.

41 changes: 28 additions & 13 deletions yarn-project/aztec-cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs';
import { createEthereumChain, deployL1Contracts } from '@aztec/ethereum';
import { DebugLogger, Logger } from '@aztec/foundation/log';
import { ContractAbi } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/aztec.js';
import { AztecAddress, AztecRPC } from '@aztec/aztec.js';
import { encodeArgs } from './cli_encoder.js';

/**
Expand All @@ -22,7 +22,7 @@ export async function deployAztecContracts(
) {
const account = !privateKey ? mnemonicToAccount(mnemonic!) : privateKeyToAccount(`0x${privateKey}`);
const chain = createEthereumChain(rpcUrl, apiKey);
await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger);
return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger);
}

/**
Expand All @@ -42,12 +42,36 @@ export function getContractAbi(fileDir: string, log: Logger) {
return contractAbi;
}

/**
* Utility to select a TX sender either from user input
* or from the first account that is found in an Aztec RPC instance.
* @param client - The Aztec RPC instance that will be checked for an account.
* @param _from - The user input.
* @returns An Aztec address. Will throw if one can't be found in either options.
*/
export async function getTxSender(client: AztecRPC, _from?: string) {
let from: AztecAddress;
if (_from) {
try {
from = AztecAddress.fromString(_from);
} catch {
throw new Error(`Invalid option 'from' passed: ${_from}`);
}
} else {
const accounts = await client.getAccounts();
if (!accounts.length) {
throw new Error('No accounts found in Aztec RPC insance.');
}
from = accounts[0];
}
return from;
}

/**
* Performs necessary checks, conversions & operations to call a contract fn from the CLI.
* @param contractFile - Directory of the compiled contract ABI.
* @param _contractAddress - Aztec Address of the contract.
* @param functionName - Name of the function to be called.
* @param _from - The caller's address.
* @param _functionArgs - Arguments to call the function with.
* @param log - Logger instance that will output to the CLI
* @returns Formatted contract address, function arguments and caller's aztec address.
Expand All @@ -56,7 +80,6 @@ export function prepTx(
contractFile: string,
_contractAddress: string,
functionName: string,
_from: string,
_functionArgs: string[],
log: Logger,
) {
Expand All @@ -73,14 +96,6 @@ export function prepTx(
}

const functionArgs = encodeArgs(_functionArgs, functionAbi.parameters);
let from;
if (_from) {
try {
from = AztecAddress.fromString(_from);
} catch {
throw new Error(`Unable to parse caller address ${_from}.`);
}
}

return { contractAddress, functionArgs, from, contractAbi };
return { contractAddress, functionArgs, contractAbi };
}
1 change: 1 addition & 0 deletions yarn-project/aztec-rpc/src/aztec_rpc/aztec_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export interface AztecRPC {
): Promise<AztecAddress>;
getAccounts(): Promise<AztecAddress[]>;
getAccountPublicKey(address: AztecAddress): Promise<Point>;
getAccountAddress(publicKey: Point): Promise<AztecAddress>;
addContracts(contracts: DeployedContract[]): Promise<void>;
/**
* Is an L2 contract deployed at this address?
Expand Down
37 changes: 34 additions & 3 deletions yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,23 @@ export class AztecRPCServer implements AztecRPC {
* @returns A Promise resolving to the Point instance representing the public key.
*/
public getAccountPublicKey(address: AztecAddress): Promise<Point> {
const account = this.#ensureAccount(address);
const account = this.#ensureAccountAddress(address);
return Promise.resolve(account.getPublicKey());
}

/**
* Retrieve the address associated with a public key.
* Throws an error if the account is not found in the key store.
*
* @param publicKey - The Point instance representing the account public key.
* @returns A Promise resolving to the Aztec Address.
*/
public getAccountAddress(publicKey: Point): Promise<AztecAddress> {
// const account = this.#ensureAccount(address);
const account = this.#ensureAccountPublicKey(publicKey);
return Promise.resolve(account.getAddress());
}

/**
* Retrieves the storage data at a specified contract address and storage slot.
* The returned data is an array of note preimage items, with each item containing its value.
Expand Down Expand Up @@ -382,7 +395,7 @@ export class AztecRPCServer implements AztecRPC {
throw new Error('No accounts available in the key store.');
}

return this.#ensureAccount(address);
return this.#ensureAccountAddress(address);
}

/**
Expand All @@ -393,7 +406,7 @@ export class AztecRPCServer implements AztecRPC {
* @returns The account state associated with the given address.
* @throws If the account is unknown or not found in the synchroniser.
*/
#ensureAccount(account: AztecAddress) {
#ensureAccountAddress(account: AztecAddress) {
const accountState = this.synchroniser.getAccount(account);
if (!accountState) {
throw new Error(`Unknown account: ${account.toShortString()}.`);
Expand All @@ -402,6 +415,24 @@ export class AztecRPCServer implements AztecRPC {
return accountState;
}

/**
* Ensures the given account public key exists in the synchroniser.
* Retrieves the account state for the provided address and throws an error if the account is not found.
*
* @param account - The public key.
* @returns The account state associated with the given address.
* @throws If the account is unknown or not found in the synchroniser.
*/
#ensureAccountPublicKey(account: Point) {
const accountState = this.synchroniser.getAccountByPublicKey(account);

if (!accountState) {
throw new Error(`Unknown account: ${account.toShortString()}.`);
}

return accountState;
}

/**
* Returns the information about the server's node
* @returns - The node information.
Expand Down
18 changes: 17 additions & 1 deletion yarn-project/aztec-rpc/src/synchroniser/synchroniser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AztecNode } from '@aztec/aztec-node';
import { Fr } from '@aztec/circuits.js';
import { Fr, Point } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { createDebugLogger } from '@aztec/foundation/log';
import { InterruptableSleep } from '@aztec/foundation/sleep';
Expand Down Expand Up @@ -174,6 +174,11 @@ export class Synchroniser {
abi = SchnorrAccountContractAbi,
keyStore: KeyStore,
) {
// check if account exists
const account = this.getAccount(address);
if (account) {
return account;
}
const accountState = new AccountState(
publicKey,
keyStore,
Expand All @@ -198,6 +203,17 @@ export class Synchroniser {
return this.accountStates.find(as => as.getAddress().equals(account));
}

/**
* Retrieve an account state by its AztecAddress from the list of managed account states.
* If no account state with the given address is found, returns undefined.
*
* @param account - The AztecAddress instance representing the account to search for.
* @returns The AccountState instance associated with the provided AztecAddress or undefined if not found.
*/
public getAccountByPublicKey(account: Point) {
return this.accountStates.find(as => as.getPublicKey().equals(account));
}

/**
* Retrieve a shallow copy of the array containing all account states.
* The returned array includes all AccountState instances added to the synchronizer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
createAccounts,
createAztecRpcClient,
getL1ContractAddresses,
pointToPublicKey,
AztecAddress,
EthAddress,
Fr,
Expand All @@ -24,7 +23,7 @@ import { UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-artifacts';
/**
* Type representation of a Public key's coordinates.
*/
type PublicKey = {
type BigIntPublicKey = {
/** Public key X coord */
x: bigint;
/** Public key Y coord */
Expand Down Expand Up @@ -72,7 +71,7 @@ let wallet: Wallet;
* Deploys all l1 / l2 contracts
* @param ownerPub - Public key of deployer.
*/
async function deployAllContracts(ownerPub: PublicKey) {
async function deployAllContracts(ownerPub: BigIntPublicKey) {
const l1ContractsAddresses = await getL1ContractAddresses(aztecRpcUrl);
logger('Deploying DAI Portal, initializing and deploying l2 contract...');
const daiContracts = await deployAndInitializeNonNativeL2TokenContracts(
Expand Down Expand Up @@ -146,7 +145,7 @@ async function deployAllContracts(ownerPub: PublicKey) {

const getL2BalanceOf = async (aztecRpcClient: AztecRPC, owner: AztecAddress, l2Contract: any) => {
const ownerPublicKey = await aztecRpcClient.getAccountPublicKey(owner);
const [balance] = await l2Contract.methods.getBalance(pointToPublicKey(ownerPublicKey)).view({ from: owner });
const [balance] = await l2Contract.methods.getBalance(ownerPublicKey.toBigInts()).view({ from: owner });
return balance;
};

Expand All @@ -170,8 +169,8 @@ const transferWethOnL2 = async (
const transferTx = wethL2Contract.methods
.transfer(
transferAmount,
pointToPublicKey(await aztecRpcClient.getAccountPublicKey(ownerAddress)),
pointToPublicKey(await aztecRpcClient.getAccountPublicKey(receiver)),
(await aztecRpcClient.getAccountPublicKey(ownerAddress)).toBigInts(),
(await aztecRpcClient.getAccountPublicKey(receiver)).toBigInts(),
)
.send({ from: ownerAddress });
await transferTx.isMined(0, 0.5);
Expand All @@ -186,10 +185,10 @@ const transferWethOnL2 = async (
async function main() {
logger('Running L1/L2 messaging test on HTTP interface.');

wallet = await createAccounts(aztecRpcClient, privateKey!, 2);
wallet = await createAccounts(aztecRpcClient, privateKey!, Fr.random(), 2);
const accounts = await wallet.getAccounts();
const [owner, receiver] = accounts;
const ownerPub = pointToPublicKey(await aztecRpcClient.getAccountPublicKey(owner));
const ownerPub = (await aztecRpcClient.getAccountPublicKey(owner)).toBigInts();

const result = await deployAllContracts(ownerPub);
const {
Expand Down
35 changes: 14 additions & 21 deletions yarn-project/aztec-sandbox/src/examples/zk_token_contract.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import {
Contract,
ContractDeployer,
Wallet,
createAccounts,
createAztecRpcClient,
pointToPublicKey,
} from '@aztec/aztec.js';
import { AztecAddress, Point } from '@aztec/circuits.js';
import { Contract, ContractDeployer, Wallet, createAccounts, createAztecRpcClient } from '@aztec/aztec.js';
import { AztecAddress, Fr, Point } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';
import { ZkTokenContractAbi } from '@aztec/noir-contracts/examples';

Expand All @@ -30,7 +23,7 @@ const SECONDARY_AMOUNT = 33n;
async function deployZKContract(pubKeyPoint: Point) {
logger('Deploying L2 contract...');
const deployer = new ContractDeployer(ZkTokenContractAbi, aztecRpcClient);
const tx = deployer.deploy(INITIAL_BALANCE, pointToPublicKey(pubKeyPoint)).send();
const tx = deployer.deploy(INITIAL_BALANCE, pubKeyPoint.toBigInts()).send();
const receipt = await tx.getReceipt();
const contract = new Contract(receipt.contractAddress!, ZkTokenContractAbi, wallet);
await tx.isMined();
Expand All @@ -47,7 +40,7 @@ async function deployZKContract(pubKeyPoint: Point) {
* @returns The owner's current balance of the token.
*/
async function getBalance(contract: Contract, ownerKey: Point, ownerAddress: AztecAddress) {
const [balance] = await contract.methods.getBalance(pointToPublicKey(ownerKey)).view({ from: ownerAddress });
const [balance] = await contract.methods.getBalance(ownerKey.toBigInts()).view({ from: ownerAddress });
return balance;
}

Expand All @@ -57,30 +50,30 @@ async function getBalance(contract: Contract, ownerKey: Point, ownerAddress: Azt
async function main() {
logger('Running ZK contract test on HTTP interface.');

wallet = await createAccounts(aztecRpcClient, privateKey, 1);
wallet = await createAccounts(aztecRpcClient, privateKey, Fr.random(), 2);
const accounts = await aztecRpcClient.getAccounts();
logger(`Created ${accounts.length} accounts`);
const [ownerAddress, address2] = accounts;
const ownerPubKeyPoint = await aztecRpcClient.getAccountPublicKey(ownerAddress);
const address2PubKeyPoint = await aztecRpcClient.getAccountPublicKey(address2);
logger(`Created account ${ownerAddress.toString()} with public key ${ownerPubKeyPoint.toString()}`);
logger(`Created ${accounts.length} accounts`);

const ownerPubKeyPoint = await wallet.getAccountPublicKey(ownerAddress);
const address2PubKeyPoint = await wallet.getAccountPublicKey(address2);
logger(`Created Owner account ${ownerAddress.toString()} with public key ${ownerPubKeyPoint.toString()}`);

const zkContract = await deployZKContract(ownerPubKeyPoint);
const [balance1] = await zkContract.methods
.getBalance(pointToPublicKey(ownerPubKeyPoint))
.view({ from: ownerAddress });
const [balance1] = await zkContract.methods.getBalance(ownerPubKeyPoint.toBigInts()).view({ from: ownerAddress });
logger(`Initial owner balance: ${balance1}`);

// Mint more tokens
logger(`Minting ${SECONDARY_AMOUNT} more coins`);
const mintTx = zkContract.methods.mint(SECONDARY_AMOUNT, ownerPubKeyPoint).send({ from: ownerAddress });
const mintTx = zkContract.methods.mint(SECONDARY_AMOUNT, ownerPubKeyPoint.toBigInts()).send({ from: ownerAddress });
await mintTx.isMined(0, 0.5);
const balanceAfterMint = await getBalance(zkContract, ownerPubKeyPoint, ownerAddress);
logger(`Owner's balance is now: ${balanceAfterMint}`);

// Perform a transfer
logger(`Transferring ${SECONDARY_AMOUNT} tokens from owner to another account.`);
const transferTx = zkContract.methods
.transfer(SECONDARY_AMOUNT, ownerPubKeyPoint, address2PubKeyPoint)
.transfer(SECONDARY_AMOUNT, ownerPubKeyPoint.toBigInts(), address2PubKeyPoint.toBigInts())
.send({ from: ownerAddress });
await transferTx.isMined(0, 0.5);
const balanceAfterTransfer = await getBalance(zkContract, ownerPubKeyPoint, ownerAddress);
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/aztec.js/src/account_impl/account_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import { AccountImplementation } from './index.js';
* A concrete account implementation that manages multiple accounts.
*/
export class AccountCollection implements AccountImplementation {
private accounts: Map<AztecAddress, AccountImplementation> = new Map();
private accounts: Map<string, AccountImplementation> = new Map();

/**
* Registers an account implementation against an aztec address
* @param addr - The aztec address agianst which to register the implementation.
* @param impl - The account implementation to be registered.
*/
public registerAccount(addr: AztecAddress, impl: AccountImplementation) {
this.accounts.set(addr, impl);
this.accounts.set(addr.toString(), impl);
}

getAddress(): AztecAddress {
if (!this.accounts) throw new Error(`No accounts registered`);
return this.accounts.keys().next().value as AztecAddress;
return AztecAddress.fromString(this.accounts.keys().next().value as string);
}

/**
Expand All @@ -34,7 +34,7 @@ export class AccountCollection implements AccountImplementation {
): Promise<TxExecutionRequest> {
// TODO: Check all executions have the same origin
const sender = executions[0].from;
const impl = this.accounts.get(sender);
const impl = this.accounts.get(sender.toString());
if (!impl) throw new Error(`No account implementation registered for ${sender}`);
return impl.createAuthenticatedTxRequest(executions, txContext);
}
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/aztec.js/src/auth/ecdsa.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { secp256k1 } from '@noble/curves/secp256k1';
import { EcdsaSignature } from '@aztec/circuits.js/barretenberg';

import { AztecAddress } from '../index.js';
import { AuthPayload } from './index.js';

import { EntrypointPayload } from '../account_impl/account_contract.js';
import { EcdsaSignature } from '@aztec/circuits.js/barretenberg';

/**
* An ecdsa implementation of auth provider.
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/auth/schnorr.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AuthPayload, AztecAddress, EntrypointPayload } from '@aztec/aztec.js';
import { Schnorr } from '@aztec/circuits.js/barretenberg';
import { AuthPayload, AztecAddress, EntrypointPayload } from '@aztec/aztec.js';

/**
* Implementation of a schnorr signature provider
Expand Down
Loading

0 comments on commit 8cf3be5

Please sign in to comment.