From 99d0c0650231ffcd55c66e3d83d1f5d28b99c63b Mon Sep 17 00:00:00 2001 From: James Zaki Date: Tue, 14 May 2024 14:41:48 +0100 Subject: [PATCH] docs: Re-add and update accounts docs (#6345) Reverts commit a687fbe62e207388bf38dc8593faf56de77fb436. Brings account docs up to date. closes https://github.com/AztecProtocol/dev-rel/issues/253 --------- Co-authored-by: josh crites Co-authored-by: josh crites --- .../resources/common_patterns/authwit.md | 2 +- .../accounts/write_accounts_contract.md | 26 +++++++++++++++---- docs/docs/developers/wallets/architecture.md | 4 +++ .../wallets/creating_schnorr_accounts.md | 4 +++ docs/docs/developers/wallets/main.md | 4 ++- docs/docs/learn/concepts/accounts/keys.md | 2 +- docs/docs/learn/concepts/accounts/main.md | 10 ++++++- 7 files changed, 43 insertions(+), 9 deletions(-) diff --git a/docs/docs/developers/contracts/resources/common_patterns/authwit.md b/docs/docs/developers/contracts/resources/common_patterns/authwit.md index f1649d731ef..334eba3e240 100644 --- a/docs/docs/developers/contracts/resources/common_patterns/authwit.md +++ b/docs/docs/developers/contracts/resources/common_patterns/authwit.md @@ -82,7 +82,7 @@ Both return the value `0xabf64ad4` (`IS_VALID` selector) for a successful authen As part of [Aztec.nr](https://aztec.nr), we are providing a library that can be used to implement authentication witness for your contracts. -This library also provides a basis for account implementations such that these can more easily implement authentication witness. +This library also provides a basis for account implementations such that these can more easily implement authentication witness. For more on the wallets, see [writing an account contract](../../writing_contracts/accounts/write_accounts_contract.md). For our purposes here (not building a wallet), the most important part of the library is the `auth` utility which exposes a couple of helper methods for computing the action hash, retrieving witnesses, validating them and emitting the nullifier. diff --git a/docs/docs/developers/contracts/writing_contracts/accounts/write_accounts_contract.md b/docs/docs/developers/contracts/writing_contracts/accounts/write_accounts_contract.md index 252b752cc84..2d39c02e8e4 100644 --- a/docs/docs/developers/contracts/writing_contracts/accounts/write_accounts_contract.md +++ b/docs/docs/developers/contracts/writing_contracts/accounts/write_accounts_contract.md @@ -1,6 +1,5 @@ --- -title: How to write an accounts contract -draft: true +title: Writing an Account Contract --- This tutorial will take you through the process of writing your own account contract in Aztec.nr, along with the Typescript glue code required for using it within a wallet. @@ -24,13 +23,13 @@ Every time a transaction payload is passed to this account contract's `entrypoin For the sake of simplicity, we will hardcode the signing public key into the contract, but you could store it [in a private note](../../../../learn/concepts/accounts/keys.md#using-a-private-note), [in an immutable note](../../../../learn/concepts/accounts/keys.md#using-an-immutable-private-note), or [on a separate keystore](../../../../learn/concepts/accounts/keys.md#using-a-separate-keystore), to mention a few examples. -## The account contract +## Contract Let's start with the account contract itself in Aztec.nr. Create [a new Aztec.nr contract project](../../main.md) that will contain a file with the code for the account contract, with a hardcoded public key: #include_code contract noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr rust -The important part of this contract is the `entrypoint` function, which will be the first function executed in any transaction originated from this account. This function has two main responsibilities: authenticating the transaction and executing calls. It receives a `payload` with the list of function calls to execute, and requests a corresponding auth witness from an oracle to validate it. You will find this logic implemented in the `AccountActions` module, which use the `AppPayload` and `FeePayload` structs: +The important part of this contract is the `entrypoint` function, which will be the first function executed in any transaction originated from this account. This function has two main responsibilities: authenticating the transaction and executing calls. It receives a `payload` with the list of function calls to execute, and requests a corresponding [authentication witness](../../../../learn/concepts/accounts/authwit.md) from an oracle to validate it. Authentication witnesses are used for authorizing actions for an account, whether it is just checking a signature, like in this case, or granting authorization for another account to act on an accounts behalf (e.g. token approvals). You will find this logic implemented in the `AccountActions` module, which use the `AppPayload` and `FeePayload` structs: #include_code entrypoint noir-projects/aztec-nr/authwit/src/account.nr rust @@ -48,7 +47,17 @@ The `AccountActions` module provides default implementations for most of the acc For our account contract, we will take the hash of the action to authorize, request the corresponding auth witness from the oracle, and validate it against our hardcoded public key. If the signature is correct, we authorize the action. -## The typescript side of things +### Fee Abstraction + +The `FeePayload`, being distinct from the `AppPayload`, allows for fee abstraction, meaning the account paying the fee for the transaction can be different than the account that is initiating the transaction. This is also useful for maintaining privacy, as fee payments on the network must be public. For example, Alice could pay a relayer transaction fees in private, and the relayer could pay the transaction fee in public. This also allows for accounts without a fee paying asset to use a non-fee paying asset to pay for fees, provided they can find a relayer willing to accept a non-fee paying asset as payment (or do it for free). You can read more about that works in the protocol specification on fees [here](../../../../protocol-specs/gas-and-fees/tx-setup-and-teardown.md). + +### Nonce Abstraction + +The protocol enforces uniqueness of transactions by checking that the transaction hash is unique. Transactions with the same transaction hash will be rejected. Handling transaction ordering via nonces is left to the account contract implementation. Account contracts can require incremental nonces, or have no requirements at all and not enforce transaction ordering. + +A side-effect of not having nonces at the protocol level is that it is not possible to cancel pending transactions by submitting a new transaction with higher fees and the same nonce. + +## Typescript 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 from `@aztec/aztec.js`: @@ -89,3 +98,10 @@ To make sure that we are actually validating the provided signature in our accou #include_code account-contract-fails yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts typescript Lo and behold, we get `Error: Assertion failed: 'verification == true'` when running the snippet above, pointing to the line in our account contract where we verify the Schnorr signature. + +## Resources + +Account contracts source code: + +- [ECDSA signer account contract](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr) +- [Schnorr signer account contract](https://github.com/AztecProtocol/aztec-packages/tree/#include_aztec_version/noir-projects/noir-contracts/contracts/schnorr_account_contract) diff --git a/docs/docs/developers/wallets/architecture.md b/docs/docs/developers/wallets/architecture.md index a02f42cf2e3..bd9d6beeb6d 100644 --- a/docs/docs/developers/wallets/architecture.md +++ b/docs/docs/developers/wallets/architecture.md @@ -6,6 +6,8 @@ This page talks about the architecture of a wallet in Aztec. To get an overview about wallets in Aztec, [go here](./main.md). +To learn how to write an accounts contract, [go here](../contracts/writing_contracts/accounts/write_accounts_contract.md). + To create a schnorr account in the sandbox, [go here](./creating_schnorr_accounts.md). Wallets expose to dapps an interface that allows them to act on behalf of the user, such as querying private state or sending transactions. Bear mind that, as in Ethereum, wallets should require user confirmation whenever carrying out a potentially sensitive action requested by a dapp. @@ -26,6 +28,8 @@ The account interface is used for creating an _execution request_ out of one or #include_code account-interface yarn-project/aztec.js/src/account/interface.ts typescript +Refer to the page on [writing an account contract](../contracts/writing_contracts/accounts/write_accounts_contract.md) for an example on how to implement this interface. + ## PXE interface A wallet exposes the PXE interface to dapps by running an PXE instance. The PXE requires a keystore and a database implementation for storing keys, private state, and recipient encryption public keys. diff --git a/docs/docs/developers/wallets/creating_schnorr_accounts.md b/docs/docs/developers/wallets/creating_schnorr_accounts.md index 0da79ddbce2..a2b4cefd98f 100644 --- a/docs/docs/developers/wallets/creating_schnorr_accounts.md +++ b/docs/docs/developers/wallets/creating_schnorr_accounts.md @@ -57,3 +57,7 @@ If you were looking at your terminal that is running the Sandbox you should have This is because the Sandbox will have simulated the deployment of both contracts, executed the private kernel circuit for each account deployment and later on submitted the 2 transactions to the pool. The sequencer will have picked them up and inserted them into an L2 block and executed the recursive rollup circuits before publishing the L2 block on L1 (in our case Anvil). Once this has completed, the L2 block is retrieved and pulled down to the PXE so that any new account state can be decrypted. + +## Next Steps + +Check out our section on [Writing your own Account Contract](../contracts/writing_contracts/accounts/write_accounts_contract.md) leveraging our account abstraction diff --git a/docs/docs/developers/wallets/main.md b/docs/docs/developers/wallets/main.md index 0a072867478..a35ce8a7f8f 100644 --- a/docs/docs/developers/wallets/main.md +++ b/docs/docs/developers/wallets/main.md @@ -4,6 +4,8 @@ title: Wallets In this page we will cover the main responsibilities of a wallet in the Aztec network. +Refer to [_writing an account contract_](../contracts/writing_contracts/accounts/write_accounts_contract.md) for a tutorial on how to write a contract to back a user's account. + Go to [\_wallet architecture](./architecture.md) for an overview of its architecture and a reference on the interface a wallet must implement. 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. @@ -16,7 +18,7 @@ In addition to these usual responsibilities, wallets in Aztec also need to track The first step for any wallet is to let the user set up their [accounts](../../learn/concepts/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, 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](../contracts/writing_contracts/accounts/write_accounts_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](../../learn/concepts/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. diff --git a/docs/docs/learn/concepts/accounts/keys.md b/docs/docs/learn/concepts/accounts/keys.md index 570ef9fb0d9..1cf42aa3755 100644 --- a/docs/docs/learn/concepts/accounts/keys.md +++ b/docs/docs/learn/concepts/accounts/keys.md @@ -15,7 +15,7 @@ This is a snippet of our Schnorr Account contract implementation, which uses Sch #include_code entrypoint /noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust -Still, different accounts may use different signing schemes, may require multi-factor authentication, or _may not even use signing keys_ and instead rely on other authentication mechanisms. +Still, different accounts may use different signing schemes, may require multi-factor authentication, or _may not even use signing keys_ and instead rely on other authentication mechanisms. Read [how to write an account contract](../../../developers/contracts/writing_contracts/accounts/write_accounts_contract.md) for a full example of how to manage authentication. Furthermore, and since signatures are fully abstracted, how the key is stored in the contract is abstracted as well and left to the developer of the account contract. Here are a few ideas on how to store them, each with their pros and cons. diff --git a/docs/docs/learn/concepts/accounts/main.md b/docs/docs/learn/concepts/accounts/main.md index 34d5f1e67cb..3dc303fd5e8 100644 --- a/docs/docs/learn/concepts/accounts/main.md +++ b/docs/docs/learn/concepts/accounts/main.md @@ -70,6 +70,8 @@ def entryPoint(payload): enqueueCall(to, data, value, gasLimit); ``` +Read more about how to write an account contract [here](../../../developers/contracts/writing_contracts/accounts/write_accounts_contract.md). + ### Account contracts and wallets Account contracts are tightly coupled to the wallet software that users use to interact with the protocol. Dapps submit to the wallet software one or more function calls to be executed (eg "call swap in X contract"), and the wallet encodes and authenticates the request as a valid payload for the user's account contract. The account contract then validates the request encoded and authenticated by the wallet, and executes the function calls requested by the dapp. @@ -111,6 +113,10 @@ The protocol requires that every account is a contract for the purposes of sendi However, this is not required when sitting on the receiving end. A user can deterministically derive their address from their encryption public key and the account contract they intend to deploy, and share this address with other users that want to interact with them _before_ they deploy the account contract. +### Account contract deployment + +Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending a fee paying asset to their account contract address (which can be derived deterministically, as mentioned above), so they have funds to pay for the deployment. Alternatively, the fee can be paid for by another account, using [fee abstraction](#fee-management). + ### Authorizing actions Account contracts are also expected, though not required by the protocol, to implement a set of methods for authorizing actions on behalf of the user. During a transaction, a contract may call into the account contract and request the user authorization for a given action, identified by a hash. This pattern is used, for instance, for transferring tokens from an account that is not the caller. @@ -133,6 +139,8 @@ NOTE: While we entertained the idea of abstracting note encryption, where accoun ### Fee management -Fees are not implemented in the protocol at the time of this writing. Our goal is to abstract fee payments as well. This means that a transaction, in order to be considered valid, must prove that it has locked enough funds to pay for itself. However, this does not mandate where those funds come from, opening the door for easy implementation of paymasters or payment-in-kind via on-the-fly swaps. +In order to be considered valid, an account must prove that it has locked enough funds to pay for itself. However, this does not mandate where those funds come from. This fee abstraction allows for easy implementation of paymasters or payment-in-kind via on-the-fly swaps. However, there is one major consideration around public execution reverts. In the current design, if one of the public function executions enqueued in a transaction fails, then the entire transaction is reverted. But reverting the whole transaction would also revert the fee payment, and leave the sequencer with their hands empty after running the public execution. This means we will need to enshrine an initial verification and fee payment phase that is _not_ reverted if public execution fails. + +You can read the latest information about fees in the [protocol specs](../../../protocol-specs/gas-and-fees/index.md).