Skip to content

Commit

Permalink
chore: aztec js circulars (AztecProtocol#3723)
Browse files Browse the repository at this point in the history
* This attempts to unpick aztec.js.
* Unfortunately, there was some heavy spaghettification here. There was
no clear separation between most of the modules, with a lot of circular
references.
* I've had to move things around to get the structure sound, but now
there are things in places that are named unintuitively. One major
example being `Wallet` now being in the `account` module.
* I think simply renaming some things, or e.g. introducing another
module with tighter scope is the next step.
* I flattened aztec.js a bit to try and make some sense. I also
introduced `create_account` as the highest level module.
* This definitely is not "complete" in terms of fixing the
names/comments etc, but if agreed it's a good first step we can merge
and iterate.

Here are some notes I took as I picked apart. Each module and its
dependencies:

```
account:

account_contract:
	account

contract:
	account

contract_deployer:
	contract

wallet:
	account
	account_contract
	contract

account_manager:
	contract
	contract_deployer
	wallet
	account
	account_contract

create_account:
	account_manager
	wallet
```
  • Loading branch information
charlielye authored Dec 21, 2023
1 parent a1c701d commit 378407d
Show file tree
Hide file tree
Showing 55 changed files with 331 additions and 2,656 deletions.
12 changes: 0 additions & 12 deletions build_manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,6 @@ aztec-faucet:
dependencies:
- yarn-project-prod

aztec-node:
buildDir: yarn-project
projectDir: yarn-project/aztec-node
dependencies:
- yarn-project-prod

p2p-bootstrap:
buildDir: yarn-project
projectDir: yarn-project/p2p-bootstrap
dependencies:
- yarn-project-prod

cli:
buildDir: yarn-project
projectDir: yarn-project/cli
Expand Down
7 changes: 3 additions & 4 deletions docs/docs/dev_docs/wallets/main.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Wallets

Wallets are the applications through which users manage their accounts. Users rely on wallets to browse through their accounts, monitor their balances, and create new accounts. Wallets also store seed phrases and private keys, or interact with external keystores such as hardware wallets.
Wallets are the applications through which users manage their accounts. Users rely on wallets to browse through their accounts, monitor their balances, and create new accounts. Wallets also store seed phrases and private keys, or interact with external keystores such as hardware wallets.

Wallets also provide an interface for dapps. Dapps may request access to see the user accounts, in order to show the state of those accounts in the context of the application, and request to send transactions from those accounts as the user interacts with the dapp.

Expand All @@ -10,9 +10,9 @@ In this page we will cover the main responsibilities of a wallet in the Aztec ne

## Account setup

The first step for any wallet is to let the user set up their [accounts](../../concepts/foundation/accounts/main.md). An account in Aztec is represented on-chain by its corresponding account contract that the user must deploy to begin interacting with the network. This account contract dictates how transactions are authenticated and executed.
The first step for any wallet is to let the user set up their [accounts](../../concepts/foundation/accounts/main.md). An account in Aztec is represented on-chain by its corresponding account contract that the user must deploy to begin interacting with the network. This account contract dictates how transactions are authenticated and executed.

A wallet must support at least one specific [account contract implementation](./writing_an_account_contract.md), which means being able to deploy such a contract, as well as interacting with it when sending transactions. Code-wise, this requires [implementing the `AccountContract` interface](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/account/contract/index.ts).
A wallet must support at least one specific [account contract implementation](./writing_an_account_contract.md), which means being able to deploy such a contract, as well as interacting with it when sending transactions. Code-wise, this requires [implementing the `AccountContract` interface](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/account_contract/index.ts).

Note that users must be able to receive funds in Aztec before deploying their account. A wallet should let a user generate a [deterministic complete address](../../concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys) without having to interact with the network, so they can share it with others to receive funds. This requires that the wallet pins a specific contract implementation, its initialization arguments, a deployment salt, and a privacy key. These values yield a deterministic address, so when the account contract is actually deployed, it is available at the precalculated address. Once the account contract is deployed, the user can start sending transactions using it as the transaction origin.

Expand Down Expand Up @@ -64,4 +64,3 @@ At the time of this writing, all private state is encrypted and broadcasted thro
Encrypted data blobs do not carry any public information as to whom their recipient is. Therefore, it is not possible for a remote node to identify the notes that belong to a user, and it is not possible for a wallet to query a remote node for its private state. As such, wallets need to keep a local database of their accounts private state, in order to be able to answer any queries on their private state.

Dapps may require access to the user's private state, in order to show information relevant to the current application. For instance, a dapp for a token may require access to the user's private notes in the token contract in order to display the user's balance. It is responsibility of the wallet to require authorization from the user before disclosing private state to a dapp.

2 changes: 1 addition & 1 deletion docs/docs/dev_docs/wallets/writing_an_account_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ For our account contract, we will take the hash of the action to authorize, requ

Now that we have a valid account contract, we need to write the typescript glue code that will take care of formatting and authenticating transactions so they can be processed by our contract, as well as deploying the contract during account setup. This takes the form of implementing the `AccountContract` interface:

#include_code account-contract-interface yarn-project/aztec.js/src/account/contract/index.ts typescript
#include_code account-contract-interface yarn-project/aztec.js/src/account_contract/account_contract.ts typescript

However, if you are using the default `AccountActions` module, then you can leverage the `BaseAccountContract` class and just implement the logic for generating an auth witness that matches the one you wrote in Noir:

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ RUN ARCH= && \
rm "node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \
node --version
COPY --from=builder /usr/src /usr/src
ENTRYPOINT ["/usr/bin/node"]
ENTRYPOINT ["/usr/bin/node"]
1 change: 1 addition & 0 deletions yarn-project/aztec.js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/account_contract/artifacts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { pedersenHash } from '@aztec/foundation/crypto';
import { FunctionCall, PackedArguments, emptyFunctionCall } from '@aztec/types';

// These must match the values defined in yarn-project/aztec-nr/aztec/src/entrypoint.nr
export const ACCOUNT_MAX_CALLS = 4;
const ACCOUNT_MAX_CALLS = 4;

/** Encoded function call for account contract entrypoint */
export type EntrypointFunctionCall = {
type EntrypointFunctionCall = {
// eslint-disable-next-line camelcase
/** Arguments hash for the call */
args_hash: Fr;
Expand Down Expand Up @@ -76,7 +76,7 @@ export function hashPayload(payload: EntrypointPayload) {
}

/** Flattens an entrypoint payload */
export function flattenPayload(payload: EntrypointPayload) {
function flattenPayload(payload: EntrypointPayload) {
return [
...payload.function_calls.flatMap(call => [
call.args_hash,
Expand Down
99 changes: 3 additions & 96 deletions yarn-project/aztec.js/src/account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,106 +19,13 @@
*
* @packageDocumentation
*/
import { CompleteAddress, GrumpkinPrivateKey, PXE } from '@aztec/types';
import { Fr } from '@aztec/circuits.js';

import { AccountContract, AccountWallet, AztecAddress, Fr } from '../index.js';
import { EcdsaAccountContract } from './contract/ecdsa_account_contract.js';
import { SchnorrAccountContract } from './contract/schnorr_account_contract.js';
import { SingleKeyAccountContract } from './contract/single_key_account_contract.js';
import { AccountManager } from './manager/index.js';
export { CompleteAddress } from '@aztec/types';

export * from './contract/index.js';
export * from './defaults/index.js';
export * from './utils.js';
export { AccountInterface, AuthWitnessProvider } from './interface.js';
export { AccountManager, CompleteAddress };
export * from './wallet.js';

/** A contract deployment salt. */
export type Salt = Fr | number | bigint;

/**
* Creates an Account that relies on an ECDSA signing key for authentication.
* @param pxe - An PXE server instance.
* @param encryptionPrivateKey - Grumpkin key used for note encryption.
* @param signingPrivateKey - Secp256k1 key used for signing transactions.
* @param saltOrAddress - Deployment salt or complete address if account contract is already deployed.
*/
export function getEcdsaAccount(
pxe: PXE,
encryptionPrivateKey: GrumpkinPrivateKey,
signingPrivateKey: Buffer,
saltOrAddress?: Salt | CompleteAddress,
): AccountManager {
return new AccountManager(pxe, encryptionPrivateKey, new EcdsaAccountContract(signingPrivateKey), saltOrAddress);
}

/**
* Creates an Account that relies on a Grumpkin signing key for authentication.
* @param pxe - An PXE server instance.
* @param encryptionPrivateKey - Grumpkin key used for note encryption.
* @param signingPrivateKey - Grumpkin key used for signing transactions.
* @param saltOrAddress - Deployment salt or complete address if account contract is already deployed.
*/
export function getSchnorrAccount(
pxe: PXE,
encryptionPrivateKey: GrumpkinPrivateKey,
signingPrivateKey: GrumpkinPrivateKey,
saltOrAddress?: Salt | CompleteAddress,
): AccountManager {
return new AccountManager(pxe, encryptionPrivateKey, new SchnorrAccountContract(signingPrivateKey), saltOrAddress);
}

/**
* Creates an Account that uses the same Grumpkin key for encryption and authentication.
* @param pxe - An PXE server instance.
* @param encryptionAndSigningPrivateKey - Grumpkin key used for note encryption and signing transactions.
* @param saltOrAddress - Deployment salt or complete address if account contract is already deployed.
*/
export function getUnsafeSchnorrAccount(
pxe: PXE,
encryptionAndSigningPrivateKey: GrumpkinPrivateKey,
saltOrAddress?: Salt | CompleteAddress,
): AccountManager {
return new AccountManager(
pxe,
encryptionAndSigningPrivateKey,
new SingleKeyAccountContract(encryptionAndSigningPrivateKey),
saltOrAddress,
);
}

/**
* Gets a wallet for an already registered account using Schnorr signatures with a single key for encryption and authentication.
* @param pxe - An PXE 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(
pxe: PXE,
address: AztecAddress,
signingKey: GrumpkinPrivateKey,
): Promise<AccountWallet> {
return getWallet(pxe, address, new SingleKeyAccountContract(signingKey));
}

/**
* Gets a wallet for an already registered account.
* @param pxe - PXE Service 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(
pxe: PXE,
address: AztecAddress,
accountContract: AccountContract,
): Promise<AccountWallet> {
const completeAddress = await pxe.getRegisteredAccount(address);
if (!completeAddress) {
throw new Error(`Account ${address} not found`);
}
const nodeInfo = await pxe.getNodeInfo();
const entrypoint = accountContract.getInterface(completeAddress, nodeInfo);
return new AccountWallet(pxe, entrypoint);
}
8 changes: 8 additions & 0 deletions yarn-project/aztec.js/src/account/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PXE } from '@aztec/types';

import { AccountInterface } from './interface.js';

/**
* The wallet interface.
*/
export type Wallet = AccountInterface & PXE;
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { ContractArtifact } from '@aztec/foundation/abi';
import { CompleteAddress, NodeInfo } from '@aztec/types';

import { AccountInterface } from '../interface.js';

export * from './ecdsa_account_contract.js';
export * from './schnorr_account_contract.js';
export * from './single_key_account_contract.js';
export * from './base_account_contract.js';
import { AccountInterface } from '../account/interface.js';

// docs:start:account-contract-interface
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ContractArtifact } from '@aztec/foundation/abi';
import { CompleteAddress, NodeInfo } from '@aztec/types';

import { DefaultAccountInterface } from '../defaults/default_interface.js';
import { AccountInterface, AuthWitnessProvider } from '../interface.js';
import { AccountContract } from './index.js';
import { DefaultAccountInterface } from '../account/defaults/default_interface.js';
import { AccountInterface, AuthWitnessProvider } from '../account/interface.js';
import { AccountContract } from './account_contract.js';

/**
* Base class for implementing an account contract. Requires that the account uses the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { ContractArtifact } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { AuthWitness, CompleteAddress } from '@aztec/types';

import EcdsaAccountContractArtifact from '../../artifacts/ecdsa_account_contract.json' assert { type: 'json' };
import { AuthWitnessProvider } from '../interface.js';
import { AuthWitnessProvider } from '../account/interface.js';
import EcdsaAccountContractArtifact from './artifacts/ecdsa_account_contract.json' assert { type: 'json' };
import { BaseAccountContract } from './base_account_contract.js';

/**
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/aztec.js/src/account_contract/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './account_contract.js';
export * from './ecdsa_account_contract.js';
export * from './schnorr_account_contract.js';
export * from './single_key_account_contract.js';
export * from './base_account_contract.js';
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { ContractArtifact } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { AuthWitness, CompleteAddress, GrumpkinPrivateKey } from '@aztec/types';

import SchnorrAccountContractArtifact from '../../artifacts/schnorr_account_contract.json' assert { type: 'json' };
import { AuthWitnessProvider } from '../interface.js';
import { AuthWitnessProvider } from '../account/interface.js';
import SchnorrAccountContractArtifact from './artifacts/schnorr_account_contract.json' assert { type: 'json' };
import { BaseAccountContract } from './base_account_contract.js';

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { ContractArtifact } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { AuthWitness, CompleteAddress, GrumpkinPrivateKey } from '@aztec/types';

import SchnorrSingleKeyAccountContractArtifact from '../../artifacts/schnorr_single_key_account_contract.json' assert { type: 'json' };
import { generatePublicKey } from '../../index.js';
import { AuthWitnessProvider } from '../interface.js';
import { AuthWitnessProvider } from '../account/interface.js';
import { generatePublicKey } from '../utils/index.js';
import SchnorrSingleKeyAccountContractArtifact from './artifacts/schnorr_single_key_account_contract.json' assert { type: 'json' };
import { BaseAccountContract } from './base_account_contract.js';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { FieldsOf } from '@aztec/circuits.js';
import { TxHash, TxReceipt } from '@aztec/types';

import { DefaultWaitOpts, SentTx, WaitOpts } from '../../contract/sent_tx.js';
import { Wallet } from '../../wallet/index.js';
import { Wallet } from '../account/index.js';
import { DefaultWaitOpts, SentTx, WaitOpts } from '../contract/index.js';
import { waitForAccountSynch } from './util.js';

/** Extends a transaction receipt with a wallet instance for the newly deployed contract. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import { PublicKey, getContractDeploymentInfo } from '@aztec/circuits.js';
import { Fr } from '@aztec/foundation/fields';
import { CompleteAddress, GrumpkinPrivateKey, PXE } from '@aztec/types';

import { DefaultWaitOpts } from '../../contract/sent_tx.js';
import { Salt } from '../account/index.js';
import { AccountInterface } from '../account/interface.js';
import {
AccountWalletWithPrivateKey,
ContractDeployer,
DeployMethod,
WaitOpts,
generatePublicKey,
} from '../../index.js';
import { AccountContract, Salt } from '../index.js';
import { AccountInterface } from '../interface.js';
AccountContract,
EcdsaAccountContract,
SchnorrAccountContract,
SingleKeyAccountContract,
} from '../account_contract/index.js';
import { DefaultWaitOpts, DeployMethod, WaitOpts } from '../contract/index.js';
import { ContractDeployer } from '../contract_deployer/index.js';
import { generatePublicKey } from '../utils/index.js';
import { AccountWalletWithPrivateKey } from '../wallet/index.js';
import { DeployAccountSentTx } from './deploy_account_sent_tx.js';
import { waitForAccountSynch } from './util.js';

Expand Down Expand Up @@ -154,3 +156,54 @@ export class AccountManager {
return completeAddress;
}
}

/**
* Creates an Account that relies on an ECDSA signing key for authentication.
* @param pxe - An PXE server instance.
* @param encryptionPrivateKey - Grumpkin key used for note encryption.
* @param signingPrivateKey - Secp256k1 key used for signing transactions.
* @param saltOrAddress - Deployment salt or complete address if account contract is already deployed.
*/
export function getEcdsaAccount(
pxe: PXE,
encryptionPrivateKey: GrumpkinPrivateKey,
signingPrivateKey: Buffer,
saltOrAddress?: Salt | CompleteAddress,
): AccountManager {
return new AccountManager(pxe, encryptionPrivateKey, new EcdsaAccountContract(signingPrivateKey), saltOrAddress);
}

/**
* Creates an Account that relies on a Grumpkin signing key for authentication.
* @param pxe - An PXE server instance.
* @param encryptionPrivateKey - Grumpkin key used for note encryption.
* @param signingPrivateKey - Grumpkin key used for signing transactions.
* @param saltOrAddress - Deployment salt or complete address if account contract is already deployed.
*/
export function getSchnorrAccount(
pxe: PXE,
encryptionPrivateKey: GrumpkinPrivateKey,
signingPrivateKey: GrumpkinPrivateKey,
saltOrAddress?: Salt | CompleteAddress,
): AccountManager {
return new AccountManager(pxe, encryptionPrivateKey, new SchnorrAccountContract(signingPrivateKey), saltOrAddress);
}

/**
* Creates an Account that uses the same Grumpkin key for encryption and authentication.
* @param pxe - An PXE server instance.
* @param encryptionAndSigningPrivateKey - Grumpkin key used for note encryption and signing transactions.
* @param saltOrAddress - Deployment salt or complete address if account contract is already deployed.
*/
export function getUnsafeSchnorrAccount(
pxe: PXE,
encryptionAndSigningPrivateKey: GrumpkinPrivateKey,
saltOrAddress?: Salt | CompleteAddress,
): AccountManager {
return new AccountManager(
pxe,
encryptionAndSigningPrivateKey,
new SingleKeyAccountContract(encryptionAndSigningPrivateKey),
saltOrAddress,
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { CompleteAddress, PXE, WaitOpts, retryUntil } from '../../index.js';
import { retryUntil } from '@aztec/foundation/retry';
import { CompleteAddress, PXE } from '@aztec/types';

import { WaitOpts } from '../contract/index.js';

/**
* Waits for the account to finish synchronizing with the PXE Service.
Expand Down
Loading

0 comments on commit 378407d

Please sign in to comment.