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

docs: Wallet dev docs #1746

Merged
merged 3 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion docs/docs/concepts/foundation/accounts/keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ partial_address := hash(salt, contract_code, constructor_hash)
address := hash(public_key, partial_address)
```

This public key corresponds to the privacy master key of the account. In order to manage private state, such as receiving an encrypted note, an account needs to share its partial address and public key, along with its address. This allows anyone to verify that the public key corresponds to the intended address.
This public key corresponds to the privacy master key of the account. In order to manage private state, such as receiving an encrypted note, an account needs to share its partial address and public key, along with its address. This allows anyone to verify that the public key corresponds to the intended address. We call the address, partial address, and public key of a user their **complete address**.

Contracts that are not meant to represent a user who handles private state, usually non-account contracts such as applications, do not need to provide a valid public key, and can instead just use zero to denote that they are not expected to receive private notes.

Expand Down
28 changes: 28 additions & 0 deletions docs/docs/dev_docs/wallets/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Architecture
iAmMichaelConnor marked this conversation as resolved.
Show resolved Hide resolved

Wallets expose to dapps an interface that allows them to act on behalf of the user, such as querying private state or sending transactions. As in Ethereum, wallets should require user confirmation whenever carrying out a potentially sensitive action requested by a dapp.

## Overview

Architecture-wise, a wallet is an instance of an **Aztec RPC Server** which manages user keys and private state and communicates with an **Aztec Node** for retrieving public information or broadcasting transactions. Note that the RPC server requires a local database for keeping private state, and is also expected to be continuously syncing new blocks for trial-decryption of user notes.

Additionally, a wallet must implement an **Entrypoint** interface that defines [how to create an execution request](./main.md#transaction-lifecycle) out of a user intent for the specific implementation of account contract used by the wallet. Think of the entrypoint interface as the Javascript counterpart of an account contract, or the piece of code that knows how to format and authenticate a transaction based on the rules defined in Noir by the user's account.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the specific implementation of account contract used by the wallet

Maybe before this paragraph, it would be helpful to explain this point. Is a wallet always built with one type of account contract in mind? Can a wallet be designed to work with many types of account contract? Can users customise their own account contracts and use those with any wallet, or are users restricted in their choice of account contract based on the wallet they choose to use?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of a user intent

How is a 'user intent' expressed / what does it look like?


## Entrypoint interface

The entrypoint interface is used for creating an execution request out of a set of function calls that describe user intents. Note that an account contract may not handle batching, in which case it is expected to throw if more than a single function call is requested.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a contradiction:
"...out of a set of function calls that describe user intents"
"...is expected to throw if more than a single function call is requested".
Can a user intent be more than one function call?


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

Refer to the page on [writing an account contract](./writing_an_account_contract.md) for an example on how to implement this interface.

## RPC interface

A wallet exposes the RPC interface to dapps by running an [Aztec RPC Server instance](https://github.com/AztecProtocol/aztec-packages/blob/95d1350b23b6205ff2a7d3de41a37e0bc9ee7640/yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts). The Aztec RPC Server requires a keystore and a database implementation for storing keys, private state, and recipient encryption public keys.

#include_code rpc-interface /yarn-project/types/src/interfaces/aztec_rpc.ts typescript


iAmMichaelConnor marked this conversation as resolved.
Show resolved Hide resolved



3 changes: 0 additions & 3 deletions docs/docs/dev_docs/wallets/building_a_wallet.md

This file was deleted.

53 changes: 53 additions & 0 deletions docs/docs/dev_docs/wallets/main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Wallets

Wallets are the applications through which users manage their accounts. They also implement the interface for dapps to perform actions on behalf of a user. And in addition to these usual responsibilities present in other blockchains, wallets in Aztec also need to manage privacy keys and private state, as well as producing local proofs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might help people's understanding to further elaborate on what an Ethereum wallet does.

Wallets are the applications through which users manage their accounts.

What does it mean to "manage" an account? What information does an Ethereum wallet store? What actions can a user perform with a wallet? What actions can a dapp perform? Aare there any differences in actions that a user can perform vs a dapp, when interacting with a wallet?

And in addition to these usual responsibilities

Just to add to the questions directly above, my memory is fuzzy as to what the "usual responsibilities" of an Ethereum wallet are, and so other readers might be too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wallets in Aztec also need to manage privacy keys and private state

What does it mean to "manage"? Store this data; provide this data to the dapp / user / contracts; prevent unauthorised access, anything else?


In this page we will cover the main responsibilities of a wallet in the Aztec network. Refer to [_writing an account contract_](./writing_an_account_contract.md) for a tutorial on how to write a contract to back a user's account, or to [_wallet architecture](./architecture.md) for an overview of its architecture and a reference on the interface a wallet must implement.

## 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 implemented by its corresponding account contract that the user must deploy to begin interacting with the network. 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 formatting transactions for it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An account in Aztec is implemented

I don't think the account is "implemented" by the account contract. It's more that an account contract is used to dictate tx validation rules for an account. And an account contract is an on-chain representation of a user's account.

as well as formatting transactions for it.

Pehaps: "...as well as formatting user intents into arguments that the account contract can interpret."

Perhaps my confusion comes from the use of the word "transaction". A "transaction request" results in a "transaction" being executed, and the record of that transaction having taken place can be referred-to as a "transaction". I wouldn't refer to the input data as a 'transaction', but rather a 'transaction request' or 'arguments' or 'inputs'.


However, 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 with others to receive funds. This requires that the wallet pins a specific contract implementation, its initialisation 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.
spalladino marked this conversation as resolved.
Show resolved Hide resolved

## Transaction lifecycle

Every transaction in Aztec is broadcasted to the network as its zero-knowledge proof of correct execution, in order to preserve privacy. This means that transaction proofs are generated on the wallet and not on a remote node. This is one of the biggest differences with regard to EVM chain wallets.
spalladino marked this conversation as resolved.
Show resolved Hide resolved

A wallet is responsible for first **creating** an [execution request](../../concepts/foundation/accounts/main.md#execution-requests). This means going from an intent, such as _call transfer on this contract_, to an authenticated transaction formatted for the user's account contract. As an example, given an [ECDSA-based account](https://github.com/AztecProtocol/aztec-packages/blob/95d1350b23b6205ff2a7d3de41a37e0bc9ee7640/yarn-project/noir-contracts/src/contracts/ecdsa_account_contract/src/main.nr#L1), an execution request has the account contract as `origin`, calls its `entrypoint` function, and encodes the user intent as `payload`, which is signed using ECDSA with a private key managed by the wallet.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A wallet is responsible for first creating an execution request. This means going from an intent, such as call transfer on this contract, to an authenticated transaction formatted for the user's account contract

So does a wallet include an implementation of 'aztec.js', or is a wallet just the Aztec RPC Server?
I guess I'm confused still by what an intent is, in this context. A human thought (an intent such as "call transfer on this contract") is translated into the human pressing a button, which is translated by aztec.js into a JSON RPC call to the Aztec RPC Server, which is translated into arguments that can be consumed by an account contract.

Would it be truer to say that the wallet's responsibilities actually begin when it receives a JSON RPC transaction request object, and then the wallet takes that object and formats it into a 'transaction request' format that can be read by the simulator (which then formats the transaction request again into arguments that can be read by the account contract)? (I.e. that the translation of an intent into JSON RPC format is actually done outside of the wallet, before reaching the wallet?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a takeaway from some of my questions so far, is that we'll need to be consistent about naming things, perhaps to the point of eventually needing a glossary, or links to actual javascript object descriptions.

Some terms that have come up so far over the past couple of pages (and in my brain):

Intent, transaction, authenticated transaction, execution request, transaction request, payload, call, transaction origin, account, account contract, account contract implementation, the various formattings of a 'transaction request' for various components, function call, enqueued function, function request, entrypoint, transaction formatting, transaction authentication, execution trace, side effect.


Once the execution request is created, it is **simulated** to retrieve an execution trace. This provides the user with a list of the side effects of a transaction if it is successfully mined, and a trace to be fed into the prover. During this simulation, the wallet is responsible of providing data to the virtual machine, such as private notes, encryption keys, or nullifier secrets.

:::info
Since private executions rely on a UTXO model, the side effects of a transaction cannot change when mined. However, the transaction can be dropped due to attempting to consume a private note that another transaction consumes before it is mined. Also, any side effects that arise from its public execution _can_ change, as in any EVM-based chain.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, any side effects that arise from its public execution can change, as in any EVM-based chain

Do the 'side effects that arise from its public execution' change, or is it really that those side effects aren't determined until the sequencer begins public execution?

:::

After the user greenlights the transaction, the wallet generates a **proof** for it that guarantees correct execution and hides all private information, which gets **sent** to the P2P network for inclusion in a next block.

:::warning
There are no proofs generated as of the Sandbox release. This will be included in a future release before testnet.
:::
## Key management

As in EVM-based chains, wallets are expected to manage user keys, or provide an interface to hardware wallets or alternate key stores. Keep in mind that in Aztec each account requires [two sets of keys](../../concepts/foundation/accounts/keys.md): privacy keys and authentication keys. Privacy keys are mandated by the protocol and used for encryption and nullification, whereas authentication keys are dependent on the account contract implementation rolled out by the wallet. Should the account contract support it, wallets must provide the user with the means to rotate or recover their authentication keys.
spalladino marked this conversation as resolved.
Show resolved Hide resolved

:::info
Due to limitations in the current architecture, privacy keys need to be available in the wallet software itself and cannot be punted to an external keystore. This restriction may be lifted in a future release.
:::
## Recipient encryption keys

Wallets are also expected to manage the public encryption keys of any recipients of local transactions. When creating an encrypted note for a recipient given their address, the wallet needs to provide their [complete address](../../concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys). This requires keeping a local registry of complete addresses for all recipients that the user intends to interact with.

:::info
There is no built-in mechanism for broadcasting public encryption keys at the moment. In a future release, clients may automatically track the complete address that corresponds to an account contract deployment, though it will still be needed to manually add recipients who have not yet deployed their account contracts.
:::

## Private state

Last but not least, wallets also track the user's private state. Wallets currently rely on brute force decryption, where every new block is downloaded and its encrypted data blobs are attempted to be decrypted with the user decryption keys. Whenever a blob is decrypted properly, it is added to the corresponding account's private state.

:::info
At the time of this writing, all private state is encrypted and broadcasted through the network, and eventually committed to L1. This means that a wallet can reconstruct its entire private state out of its encryption keys in the event of local data loss.
:::

Note that wallets must also scan for private state in blocks prior to their deployment, since users may have received private state before deployment.
spalladino marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions yarn-project/aztec.js/src/account/entrypoint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type CreateTxRequestOpts = {
origin?: AztecAddress;
};

// docs:start:entrypoint-interface
/**
* Represents a transaction entrypoint in an account contract.
* Knows how to assemble a transaction execution request given a set of function calls.
Expand All @@ -24,3 +25,4 @@ export interface Entrypoint {
*/
createTxExecutionRequest(executions: FunctionCall[], opts?: CreateTxRequestOpts): Promise<TxExecutionRequest>;
}
// docs:end:entrypoint-interface
2 changes: 2 additions & 0 deletions yarn-project/types/src/interfaces/aztec_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type SyncStatus = {
notes: Record<string, number>;
};

// docs:start:rpc-interface
/**
* Represents an Aztec RPC implementation.
* Provides functionality for all the operations needed to interact with the Aztec network,
Expand Down Expand Up @@ -237,3 +238,4 @@ export interface AztecRPC {
*/
getSyncStatus(): Promise<SyncStatus>;
}
// docs:end:rpc-interface