From 677e4e027332715646a8e0234527898280656a0b Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:53:10 +0200 Subject: [PATCH 01/18] chore: type fix in docs --- docs/docs/aztec/concepts/accounts/keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 7df96c9c13f..c003a349ddc 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -59,7 +59,7 @@ All the keys below are Grumpkin keys (public keys derived on the Grumpkin curve) ## Nullifier keys Whenever a note is consumed, a nullifier deterministically derived from it is emitted. -This mechanisms prevents double-spends, since nullifiers are checked by the protocol to be unique. +This mechanism prevents double-spends, since nullifiers are checked by the protocol to be unique. Now, in order to preserve privacy, a third party should not be able to link a note hash to its nullifier - this link is enforced by the note implementation. Therefore, calculating the nullifier for a note requires a secret from its owner. From 19bebc7430c45da45815e9a9d0f7481ff71bc468 Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:35:50 +0200 Subject: [PATCH 02/18] chore: docs keys page update --- docs/docs/aztec/concepts/accounts/keys.md | 170 +++++++++------------- 1 file changed, 68 insertions(+), 102 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index c003a349ddc..36386ed36f8 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -3,157 +3,123 @@ title: Keys tags: [accounts, keys] --- -The goal of this section is to give app developer a good idea what keys there are used in the system. +In this section, you will learn what keys are used in Aztec, and how the addresses are derived. -In short, there is a **nullifier key** (to spend your notes), an **incoming viewing key** (to view any notes or logs that were sent to you), an **outgoing viewing key** (to view any logs or notes you sent to another entity), a **tagging key** (to quickly find notes relevant to you) and oftentimes a signing key. A signing key is not strictly required by the protocol, but are often used with specific account contracts for authorization purposes. +## Types of keys -Each account in Aztec is backed by 4 key pairs: +Each Aztec account is backed by four key pairs: +- Nullifier keys – used to spend notes. +- Incoming viewing keys – used to encrypt a note for the recipient. +- Address keys – this is an auxiliary key used for the address derivation; it’s internally utilized by the protocol and does not require any action from developers. +- Signing keys – an optional key pair used for account authorization. -- A **nullifier key pair** used for note nullifier computation, comprising the master nullifier secret key (`nsk_m`) and master nullifier public key (`Npk_m`). -- An **incoming viewing key pair** used to encrypt a note for the recipient, consisting of the master incoming viewing secret key (`ivsk_m`) and master incoming viewing public key (`Ivpk_m`). -- An **outgoing viewing key pair** used to encrypt a note for the sender, includes the master outgoing viewing secret key (`ovsk_m`) and master outgoing viewing public key (`Ovpk_m`). -- A **tagging key pair** used to compute tags in a tagging note discovery scheme, comprising the master tagging secret key (`tsk_m`) and master tagging public key (`Tpk_m`). +The first three pairs are embedded into the protocol while the signing key is abstracted up to the account contract developer. -:::info -Key pairs are derived from a secret using a ZCash inspired scheme. -::: - -:::note -Additionally, there is typically a signing key pair which is used for authenticating the owner of the account. -However, since Aztec supports native [account abstraction](../accounts/index.md#what-is-account-abstraction) this is not defined in protocol. -Instead it's up to the account contract developer to implement it. -::: - -## Public keys retrieval +### Nullifier keys -The keys for our accounts can be retrieved from the [Private eXecution Environment (PXE)](../pxe/index.md) using the following getter in Aztec.nr: +Nullifier keys are presented as a pair of the master nullifier public key (`Npk_m`) and the master nullifier secret key (`nsk_m`). -``` -fn get_public_keys(account: AztecAddress) -> PublicKeys; -``` +To spend a note, the user computes a nullifier corresponding to this note. A nullifier is a hash of the note hash and nullifier secret key. To compute the nullifier, the user needs to prove that they know the nullifier secret key. In particular, they prove that their nullifier secret key is embedded into their address. -It is necessary to first register the user as an account in our PXE, by calling the `registerAccount` PXE endpoint using Aztec.js, providing the account's secret key and partial address. +### Incoming viewing keys -During private function execution these keys are obtained via an oracle call from PXE. +The incoming viewing public key (`Ivpk`) is used by the sender to encrypt a note for the recipient. The corresponding incoming viewing secret key (`ivsk`) is used by the recipient to decrypt the note. -## Scoped keys +When it comes to notes encryption and decryption: +- For each note, there is a randomly generated ephemeral key pair (`esk`, `Epk`) where `Epk = esk * G`. +- The `AddressPublicKey` (derived from the `Ivsk`) together with `esk` are encrypted as a secret `S`, `S = esk * AddressPublicKey`. +- `symmetric_encryption_key = hash(S)` +- `Ciphertext = aes_encrypt(note, symmetric_encryption_key)` +- The recipient gets a pair (`Epk`, `Ciphertext`) +- The recipient uses the `address_sk` to decrypt the secret: `S = Epk * address_sk`. +- The recipient uses the decrypted secret to decrypt the ciphertext. -To minimize damage of potential key leaks the keys are scoped (also called app-siloed) to the contract that requests them. -This means that the keys used for the same user in two different application contracts will be different and potential leak of the scoped keys would only affect 1 application. +### Address keys -This also allows per-application auditability. -A user may choose to disclose their incoming and outgoing viewing keys for a given application to an auditor or regulator (or for 3rd party interfaces, e.g. giving access to a block explorer to display my activity), as a means to reveal all their activity within that context, while retaining privacy across all other applications in the network. +Address keys are used for account address derivation. -In the case of nullifier keys, there is also a security reason involved. -Since the nullifier secret is exposed to the application contract to be used in the nullifier computation, the contract may accidentally or maliciously leak it. -If that happens, only the nullifier secret for that application is compromised (`nsk_app` and not `nsk_m`). +Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. -Above we mentioned that the notes typically contain `Npk_m`. -It might seem like a mistake given that the notes are nullified with `nsk_app`. -This is intentional and instead of directly trying to derive `Npk_m` from `nsk_app` we instead verify that both of the keys were derived from the same `nsk_m` in our protocol circuits. - -## Protocol key types - -All the keys below are Grumpkin keys (public keys derived on the Grumpkin curve). +:::note +Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing key) and `Tpk` (tagging key). However, formally they still exist and can be used by developers for some non-trivial design choices if needed. +::: -## Nullifier keys +### Signing keys -Whenever a note is consumed, a nullifier deterministically derived from it is emitted. -This mechanism prevents double-spends, since nullifiers are checked by the protocol to be unique. -Now, in order to preserve privacy, a third party should not be able to link a note hash to its nullifier - this link is enforced by the note implementation. -Therefore, calculating the nullifier for a note requires a secret from its owner. +Thanks to the native account abstraction, authorization logic can be implemented in an alternative way using Google authorization credentials, vanilla password logic or Face ID mechanism. In all these cases signing keys are not relevant. -An application in Aztec.nr can request a secret from the current user for computing the nullifier of a note via the `request_nullifier_secret_key` API: +However if one wants to implement authorization logic containing signatures (e.g. ECDSA or Shnorr) they will need signing keys. Usually, an account contract will validate a signature of the incoming payload against a known signing public key. -#include_code nullifier /noir-projects/aztec-nr/value-note/src/value_note.nr rust +This is a snippet of our Schnorr Account contract implementation, which uses Schnorr signatures for authentication: -Typically, `Npk_m` is stored in a note and later on, the note is nullified using the secret app-siloed version (denoted `nsk_app`). -`nsk_app` is derived by hashing `nsk_m` with the app contract address and it is necessary to present it to compute the nullifier. -Validity of `nsk_app` is verified by our protocol kernel circuits. +#include_code is_valid_impl /noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust -## Incoming viewing keys +### Storing signing keys -The public key (denoted `Ivpk`) is used to encrypt a note for a recipient and the corresponding secret key (`ivsk`) is used by the recipient during decryption. +Since signatures are fully abstracted, how the public key is stored in the contract is abstracted as well and left to the developer of the account contract. Among a few common approaches are storing the key in a private note, in an immutable private note, using shared mutable state, reusing other in-protocol keys, or a separate keystore. Below, we elaborate on these approaches. -## Outgoing viewing keys +#### Using a private note​ -App-siloed versions of outgoing viewing keys are denoted `ovsk_app` and `Ovpk_app`. -These keys are used to encrypt a note for a note sender which is necessary for reconstructing transaction history from on-chain data. -For example, during a token transfer, the token contract may dictate that the sender encrypts the note with value with the recipient's `Ivpk`, but also records the transfer with its own `Ovpk_app` for bookkeeping purposes. -If these keys were not used and a new device would be synched there would be no "direct" information available about notes that a user created for other people. +Storing the signing public key in a private note makes it accessible from the entrypoint function, which is required to be a private function, and allows for rotating the key when needed. However, keep in mind that reading a private note requires nullifying it to ensure it is up-to-date, so each transaction you send will destroy and recreate the public key so the protocol circuits can be sure that the notes are not stale. -## Tagging keys +#### Using an immutable private note​ -Used to compute tags in a tagging note discovery scheme. +Similar to using a private note, but using an immutable private note removes the need to nullify the note on every read. This generates no nullifiers and commitments per transaction. However, it does not allow the user to rotate their key if they lose it. :::note -Tagging note discovery scheme won't be present in our testnet so we are intentionally not providing you with much info yet. +When it comes to storing the signing key in a private note, there are several details that rely on the wallets: +- A note with a key is managed similar to any other private note. Wallets are expected to backup all the notes so that they can be restored on another device (e.g. if the user wants to move to another device). +- The note with the key might exist locally only (in PXE) or it can be broadcasted as an encrypted note by the wallet to itself. In the second case, this note will also exist on Aztec. ::: -## Signing keys +#### Using Shared Mutable state -As mentioned above signing keys are not defined in protocol because of [account abstraction](../accounts/index.md#what-is-account-abstraction) and instead the key scheme is defined by the account contract. - -Usually, an account contract will validate a signature of the incoming payload against a known signing public key. +:::note +By Shared Mutable we mean privately readable publicly mutable state. +::: -This is a snippet of our Schnorr Account contract implementation, which uses Schnorr signatures for authentication: +To make public state accessible privately, there should be a delay window in public state updates. One needs this window to be able to generate proofs client-side. This approach would not generate additional nullifiers and commitments for each transaction while allowing the user to rotate their key. However, this causes every transaction to now have a time-to-live determined by the frequency of the mutable shared state, as well as imposing restrictions on how fast keys can be rotated due to minimum delays. -#include_code is_valid_impl /noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust +#### Reusing some of the in-protocol keys​ -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](../../../tutorials/codealong/contract_tutorials/write_accounts_contract.md) for a full example of how to manage authentication. +It is possible to use some of the key pairs defined in protocol (e.g. incoming viewing keys) as the signing key. Since this key is part of the address preimage, it can be validated against the account contract address rather than having to store it. However, this approach is not recommended since it reduces the security of the user's account. -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. -In the following section we describe a few ways how an account contract could be architected to store signing keys. +#### Using a separate keystore​ -### Storing signing keys +Since there are no restrictions on the actions that an account contract may execute for authenticating a transaction (as long as these are all private function executions), the signing public keys can be stored in a separate keystore contract that is checked on every call. In this case, each user could keep a single contract that acts as a keystore, and have multiple account contracts that check against that keystore for authorization. This will incur a higher proving time for each transaction, but has no additional cost in terms of fees. -#### Using a private note +### Keys generation -Storing the signing public key in a private note makes it accessible from the entrypoint function, which is required to be a private function, and allows for rotating the key when needed. However, keep in mind that reading a private note requires nullifying it to ensure it is up-to-date, so each transaction you send will destroy and recreate the public key. This has the side effect of enforcing a strict ordering across all transactions, since each transaction will refer the instantiation of the private note from the previous one. +All key pairs (except for the signing keys) are generated in the [Private Execution Environment](../pxe/index.md) (PXE) when a user creates an account. PXE is also responsible for the further key management (oracle access to keys, app siloed keys derivation, etc.) -#### Using an immutable private note +### Keys derivation -Similar to using a private note, but using an immutable private note removes the need to nullify the note on every read. This generates less nullifiers and commitments per transaction, and does not enforce an order across transactions. However, it does not allow the user to rotate their key should they lose it. +All key pairs are derived using elliptic curve public-key cryptography on the [Grumpkin curve](https://hackmd.io/@aztec-network/ByzgNxBfd#2-Grumpkin---A-curve-on-top-of-BN-254-for-SNARK-efficient-group-operations). Where the secret key is represented as a scalar and the public key is represented as an elliptic curve point multiplied by that scalar. -#### Using shared state +The address private key is an exception and derived in a way described above in the section “Address keys”. -A compromise between the two solutions above is to use shared state. This would not generate additional nullifiers and commitments for each transaction while allowing the user to rotate their key. However, this causes every transaction to now have a time-to-live determined by the frequency of the mutable shared state, as well as imposing restrictions on how fast keys can be rotated due to minimum delays. +### The special case of escrow contracts -#### Reusing some of the in-protocol keys +Typically, for account contracts the public keys will be non-zero and for non-account contracts zero. -It is possible to use some of the key pairs defined in protocol (e.g. incoming viewing keys) as the signing key. -Since this key is part of the address preimage (more on this on the privacy master key section), it can be validated against the account contract address rather than having to store it. -However, this approach is not recommended since it reduces the security of the user's account. +An exception (a non-account contract which would have some of the keys non-zero) is an escrow contract. Escrow contract is a type of contract which on its own is an "owner" of a note meaning that it has a` Npk_m` registered and the notes contain this `Npk_m`. -#### Using a separate keystore +Participants in this escrow contract would then somehow get a hold of the escrow's `nsk_m` and nullify the notes based on the logic of the escrow. An example of an escrow contract is a betting contract. In this scenario, both parties involved in the bet would be aware of the escrow's `nsk_m`. The escrow would then release the reward only to the party that provides a "proof of winning". -Since there are no restrictions on the actions that an account contract may execute for authenticating a transaction (as long as these are all private function executions), the signing public keys can be stored in a [separate keystore contract](https://vitalik.ca/general/2023/06/09/three_transitions.html) that is checked on every call. This will incur in a higher proving time for each transaction, but has no additional cost in terms of fees, and allows for easier key management in a centralized contract. +### App siloed keys -### Complete address +All keys on Aztec (except for the signing keys) are app-siloed meaning they are scoped to the contract that requests them. This means that the keys used for the same user in two different application contracts will be different. -When deploying a contract, the contract address is deterministically derived using the following scheme: +App-siloed keys allow to minimize damage of potential key leaks as a leak of the scoped keys would only affect one application. - +App-siloed keys are derived from the corresponding master keys and the contract address. For example, for the app-siloed nullifier secret key: `nsk_app = hash(nsk_m, app_contract_address)`. -``` -partial_address := poseidon2("az_contract_partial_address_v1", contract_class_id, salted_initialization_hash) -public_keys_hash := poseidon2("az_public_keys_hash", Npk_m, Ivpk_m, Ovpk_m, Tpk_m) -address := poseidon2("az_contract_address_v1", public_keys_hash, partial_address) -``` +App-siloed keys are derived in PXE every time the user interacts with the application. -Typically, for account contracts the public keys will be non-zero and for non-account contracts zero. -An example of a non-account contract which would have some of the keys non-zero is an escrow contract. -Escrow contract is a type of contract which on its own is an "owner" of a note meaning that it has a `Npk_m` registered and the notes contain this `Npk_m`. -Participants in this escrow contract would then somehow get a hold of the escrow's `nsk_m` and nullify the notes based on the logic of the escrow. -An example of an escrow contract is a betting contract. In this scenario, both parties involved in the bet would be aware of the escrow's `nsk_m`. -The escrow would then release the reward only to the party that provides a "proof of winning". +App-siloed incoming viewing key also allows per-application auditability. A user may choose to disclose this key for a given application to an auditor or regulator (or for 3rd party interfaces, e.g. giving access to a block explorer to display my activity), as a means to reveal all their activity within that context, while retaining privacy across all other applications in the network. -Because of the contract address derivation scheme it is possible to check that a given set of public keys corresponds to a given address just by trying to recompute it. -Since this is commonly needed to be done when sending a note to an account we coined the term **complete address** for the collection of: +### Key rotation -1. all the user's public keys, -2. partial address, -3. contract address. +Key rotation is the process of creating new signing keys to replace existing keys. By rotating encryption keys on a regular schedule or after specific events, you can reduce the potential consequences of the key being compromised. -Once the complete address is shared with the sender, the sender can check that the address was correctly derived from the public keys and partial address and then send the notes to that address. -Because of this it is possible to send a note to an account whose account contract was not yet deployed. +On Aztec, key rotation is impossible for nullifier keys, incoming viewing keys and address keys as all of them are embedded into the address and address is unchangeable. In the meanwhile, signing keys can be rotated. From a21af4ff07cc1531618b9225ee374d9739b0714c Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Tue, 28 Jan 2025 12:36:24 +0200 Subject: [PATCH 03/18] Update docs/docs/aztec/concepts/accounts/keys.md Co-authored-by: Rahul Kothari --- docs/docs/aztec/concepts/accounts/keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 36386ed36f8..6f617bc3860 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -19,7 +19,7 @@ The first three pairs are embedded into the protocol while the signing key is ab Nullifier keys are presented as a pair of the master nullifier public key (`Npk_m`) and the master nullifier secret key (`nsk_m`). -To spend a note, the user computes a nullifier corresponding to this note. A nullifier is a hash of the note hash and nullifier secret key. To compute the nullifier, the user needs to prove that they know the nullifier secret key. In particular, they prove that their nullifier secret key is embedded into their address. +To spend a note, the user computes a nullifier corresponding to this note. A nullifier is a hash of the note hash and app siloed nullifier secret key, the latter is derived using the nullifier master secret key. To compute the nullifier, the protocol checks that the app siloed key is derived from the master key for this contract and that master nullifier public key is linked to the note owner's address. ### Incoming viewing keys From 783662a9bb813e94ef3638ff50e04a6f7156ca5b Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:30:19 +0200 Subject: [PATCH 04/18] Update keys.md --- docs/docs/aztec/concepts/accounts/keys.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 6f617bc3860..c5d1b29504b 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -36,7 +36,7 @@ When it comes to notes encryption and decryption: ### Address keys -Address keys are used for account address derivation. +Address keys are used for account [address derivation](../accounts/index.md). Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. @@ -46,7 +46,7 @@ Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing ke ### Signing keys -Thanks to the native account abstraction, authorization logic can be implemented in an alternative way using Google authorization credentials, vanilla password logic or Face ID mechanism. In all these cases signing keys are not relevant. +Thanks to the native [account abstraction](../accounts#background/index.md), authorization logic can be implemented in an alternative way using Google authorization credentials, vanilla password logic or Face ID mechanism. In all these cases signing keys are not relevant. However if one wants to implement authorization logic containing signatures (e.g. ECDSA or Shnorr) they will need signing keys. Usually, an account contract will validate a signature of the incoming payload against a known signing public key. @@ -75,7 +75,7 @@ When it comes to storing the signing key in a private note, there are several de #### Using Shared Mutable state :::note -By Shared Mutable we mean privately readable publicly mutable state. +By [Shared Mutable](../shared_state#sharedmutable/index.md) we mean privately readable publicly mutable state. ::: To make public state accessible privately, there should be a delay window in public state updates. One needs this window to be able to generate proofs client-side. This approach would not generate additional nullifiers and commitments for each transaction while allowing the user to rotate their key. However, this causes every transaction to now have a time-to-live determined by the frequency of the mutable shared state, as well as imposing restrictions on how fast keys can be rotated due to minimum delays. @@ -94,7 +94,7 @@ All key pairs (except for the signing keys) are generated in the [Private Execut ### Keys derivation -All key pairs are derived using elliptic curve public-key cryptography on the [Grumpkin curve](https://hackmd.io/@aztec-network/ByzgNxBfd#2-Grumpkin---A-curve-on-top-of-BN-254-for-SNARK-efficient-group-operations). Where the secret key is represented as a scalar and the public key is represented as an elliptic curve point multiplied by that scalar. +All key pairs are derived using elliptic curve public-key cryptography on the [Grumpkin curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md). Where the secret key is represented as a scalar and the public key is represented as an elliptic curve point multiplied by that scalar. The address private key is an exception and derived in a way described above in the section “Address keys”. @@ -114,7 +114,7 @@ App-siloed keys allow to minimize damage of potential key leaks as a leak of the App-siloed keys are derived from the corresponding master keys and the contract address. For example, for the app-siloed nullifier secret key: `nsk_app = hash(nsk_m, app_contract_address)`. -App-siloed keys are derived in PXE every time the user interacts with the application. +App-siloed keys [are derived](../storage_slots#implementation/index.md) in PXE every time the user interacts with the application. App-siloed incoming viewing key also allows per-application auditability. A user may choose to disclose this key for a given application to an auditor or regulator (or for 3rd party interfaces, e.g. giving access to a block explorer to display my activity), as a means to reveal all their activity within that context, while retaining privacy across all other applications in the network. From 81d753f19251aa5657ec3d47911491360a8e2e24 Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Tue, 28 Jan 2025 18:13:30 +0200 Subject: [PATCH 05/18] Update keys.md --- docs/docs/aztec/concepts/accounts/keys.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index c5d1b29504b..7a89dace94c 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -66,6 +66,8 @@ Storing the signing public key in a private note makes it accessible from the en Similar to using a private note, but using an immutable private note removes the need to nullify the note on every read. This generates no nullifiers and commitments per transaction. However, it does not allow the user to rotate their key if they lose it. +#include_code public_key_note noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr rust + :::note When it comes to storing the signing key in a private note, there are several details that rely on the wallets: - A note with a key is managed similar to any other private note. Wallets are expected to backup all the notes so that they can be restored on another device (e.g. if the user wants to move to another device). From 670db1cf06652b823bd72035beb53ba878a486ca Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:22:29 +0200 Subject: [PATCH 06/18] Update main.nr --- .../contracts/schnorr_account_contract/src/main.nr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 43aed0e231b..48f17c196c3 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -25,7 +25,9 @@ contract SchnorrAccount { #[storage] struct Storage { +// docs:start:public_key signing_public_key: PrivateImmutable, +// docs:end:public_key } // Constructs the contract From 1633de079d5599e2a630373b93b5c1fda0ffa63c Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:22:47 +0200 Subject: [PATCH 07/18] Update main.nr --- .../contracts/schnorr_account_contract/src/main.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 48f17c196c3..b48a5d52977 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -25,9 +25,9 @@ contract SchnorrAccount { #[storage] struct Storage { -// docs:start:public_key + // docs:start:public_key signing_public_key: PrivateImmutable, -// docs:end:public_key + // docs:end:public_key } // Constructs the contract From aace7c35d438e0cc0a6a345b47a49b07af1fde3f Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:25:59 +0200 Subject: [PATCH 08/18] Update keys.md --- docs/docs/aztec/concepts/accounts/keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 7a89dace94c..f93adda21fa 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -66,7 +66,7 @@ Storing the signing public key in a private note makes it accessible from the en Similar to using a private note, but using an immutable private note removes the need to nullify the note on every read. This generates no nullifiers and commitments per transaction. However, it does not allow the user to rotate their key if they lose it. -#include_code public_key_note noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr rust +#include_code public_key aztec-packages/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust :::note When it comes to storing the signing key in a private note, there are several details that rely on the wallets: From ab4f9db8bd9f089d8302baa0eb48b1157771c79f Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Fri, 31 Jan 2025 11:00:21 +0200 Subject: [PATCH 09/18] Update docs/docs/aztec/concepts/accounts/keys.md Co-authored-by: josh crites --- docs/docs/aztec/concepts/accounts/keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 4b50bc03245..c0cae21b071 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -46,7 +46,7 @@ Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing ke ### Signing keys -Thanks to the native [account abstraction](../accounts#background/index.md), authorization logic can be implemented in an alternative way using Google authorization credentials, vanilla password logic or Face ID mechanism. In all these cases signing keys are not relevant. +Thanks to the native [account abstraction](../accounts#background/index.md), authorization logic can be implemented in an alternative way that is up to the developer (e.g. using Google authorization credentials, vanilla password logic or Face ID mechanism). In all these cases signing keys may not be relevant. However if one wants to implement authorization logic containing signatures (e.g. ECDSA or Shnorr) they will need signing keys. Usually, an account contract will validate a signature of the incoming payload against a known signing public key. From 96a588dabb963908027653d1061cc6879e9c7207 Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Fri, 31 Jan 2025 11:19:05 +0200 Subject: [PATCH 10/18] Update docs/docs/aztec/concepts/accounts/keys.md Co-authored-by: josh crites --- docs/docs/aztec/concepts/accounts/keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index c0cae21b071..6d9293daf16 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -27,7 +27,7 @@ The incoming viewing public key (`Ivpk`) is used by the sender to encrypt a note When it comes to notes encryption and decryption: - For each note, there is a randomly generated ephemeral key pair (`esk`, `Epk`) where `Epk = esk * G`. -- The `AddressPublicKey` (derived from the `Ivsk`) together with `esk` are encrypted as a secret `S`, `S = esk * AddressPublicKey`. +- The `AddressPublicKey` (derived from the `ivsk`) together with `esk` are encrypted as a secret `S`, `S = esk * AddressPublicKey`. - `symmetric_encryption_key = hash(S)` - `Ciphertext = aes_encrypt(note, symmetric_encryption_key)` - The recipient gets a pair (`Epk`, `Ciphertext`) From 616bc3450a21a8327f410ad7dee9f043c4b955db Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Fri, 31 Jan 2025 11:24:13 +0200 Subject: [PATCH 11/18] Update keys.md --- docs/docs/aztec/concepts/accounts/keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index 6d9293daf16..f3514257787 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -38,7 +38,7 @@ When it comes to notes encryption and decryption: Address keys are used for account [address derivation](../accounts/index.md). -Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. +Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point defined as `AddressPublicKey = address_sk * G`. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. :::note Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing key) and `Tpk` (tagging key). However, formally they still exist and can be used by developers for some non-trivial design choices if needed. From 9892f3782040898e199ffcb5295da0d8abf50d7a Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Fri, 31 Jan 2025 11:34:22 +0200 Subject: [PATCH 12/18] Update keys.md --- docs/docs/aztec/concepts/accounts/keys.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index f3514257787..c8d68dc19e4 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -9,8 +9,8 @@ In this section, you will learn what keys are used in Aztec, and how the address Each Aztec account is backed by four key pairs: - Nullifier keys – used to spend notes. -- Incoming viewing keys – used to encrypt a note for the recipient. - Address keys – this is an auxiliary key used for the address derivation; it’s internally utilized by the protocol and does not require any action from developers. +- Incoming viewing keys – used to encrypt a note for the recipient. - Signing keys – an optional key pair used for account authorization. The first three pairs are embedded into the protocol while the signing key is abstracted up to the account contract developer. @@ -21,6 +21,16 @@ Nullifier keys are presented as a pair of the master nullifier public key (`Npk_ To spend a note, the user computes a nullifier corresponding to this note. A nullifier is a hash of the note hash and app siloed nullifier secret key, the latter is derived using the nullifier master secret key. To compute the nullifier, the protocol checks that the app siloed key is derived from the master key for this contract and that master nullifier public key is linked to the note owner's address. +### Address keys + +Address keys are used for account [address derivation](../accounts/index.md). + +Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point defined as `AddressPublicKey = address_sk * G`. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. + +:::note +Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing key) and `Tpk` (tagging key). However, formally they still exist and can be used by developers for some non-trivial design choices if needed. +::: + ### Incoming viewing keys The incoming viewing public key (`Ivpk`) is used by the sender to encrypt a note for the recipient. The corresponding incoming viewing secret key (`ivsk`) is used by the recipient to decrypt the note. @@ -34,16 +44,6 @@ When it comes to notes encryption and decryption: - The recipient uses the `address_sk` to decrypt the secret: `S = Epk * address_sk`. - The recipient uses the decrypted secret to decrypt the ciphertext. -### Address keys - -Address keys are used for account [address derivation](../accounts/index.md). - -Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point defined as `AddressPublicKey = address_sk * G`. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. - -:::note -Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing key) and `Tpk` (tagging key). However, formally they still exist and can be used by developers for some non-trivial design choices if needed. -::: - ### Signing keys Thanks to the native [account abstraction](../accounts#background/index.md), authorization logic can be implemented in an alternative way that is up to the developer (e.g. using Google authorization credentials, vanilla password logic or Face ID mechanism). In all these cases signing keys may not be relevant. From 46a11a8ec618598b89230dae5b2a8474405a4700 Mon Sep 17 00:00:00 2001 From: Lisa Date: Thu, 13 Feb 2025 19:50:04 +0200 Subject: [PATCH 13/18] Updating docs accounts --- docs/docs/aztec/concepts/accounts/index.md | 157 ++++++++++++--------- 1 file changed, 88 insertions(+), 69 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/index.md b/docs/docs/aztec/concepts/accounts/index.md index 4f29f273ecb..21228313369 100644 --- a/docs/docs/aztec/concepts/accounts/index.md +++ b/docs/docs/aztec/concepts/accounts/index.md @@ -4,56 +4,48 @@ sidebar_position: 1 tags: [accounts] --- -**Every account in Aztec is a smart contract** which defines the rules for whether a transaction is or is not valid. This allows implementing different schemes for transaction signing, nonce management, and fee payments. However, encryption and nullifying keys, which are specific to private blockchains, are still enshrined at the protocol level. +Aztec has native account abstraction. Every account in Aztec is a smart contract. -In this section, you’ll learn about how Aztec defines AA (account abstraction) and its correlation with encryption keys and nullifying keys. We’ll go through: +In this section, you’ll learn about Aztec’s account abstraction, Aztec accounts and address derivation, how wallets relate to accounts, and how the entrypoints are defined. -- The importance and implications of AA -- Understanding account contracts and wallets in relation to Aztec -- Concept of authorization and actions along with encryption -- The future of fee management in Aztec +## Account Abstraction (AA) -## Background +Under the Account Abstraction, the identity of a user is usually represented by a smart contract. -We'll start with the mandatory "what is AA" section that every single article on the topic has, so you can skip this if you're familiar with the topic. +Among the account parts to be abstracted are authentication (“Who I am”), authorization (“What I am allowed to do”), replay protection, fee payment, and execution. -### What is account abstraction? +Among the use cases to be unlocked by Account Abstraction are account recovery, gas sponsorship, multichain accounts, and support of signatures other than ECDSA, such as more efficient signatures (e.g. Schnorr, BLS), or more user-friendly ones (e.g. smartphone secure enclave). -We'll refer to AA as the _ability to set the validity conditions of a transaction programmatically_ ([source](https://fuel-labs.ghost.io/account-abstraction-for-everyone-else/)). [Starknet](https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/introduction/#account_abstraction) goes one step further and splits AA into three different components: +### Protocol vs application level -- Signature abstraction (defining when a signature is accepted) -- Fee abstraction (paying fees) -- Nonce abstraction (replay protection and ordering) +AA can be implemented at the protocol level, then we call it native Account Abstraction. In this case, all the accounts on the network are smart contracts. AA can also be implemented at the smart-contract level, then we call it non-native Account Abstraction. In this case, there might be both EOAs and accounts controlled by smart contracts. -In most AA schemes, the identity of a user is no longer represented by a keypair but by a contract, often called a smart contract wallet or account contract. This contract receives transaction payloads which are validated with custom logic, and then interpreted as actions to execute, like calling into another contract. +In the case of Aztec, we have native Account Abstraction. -The benefits of AA are multiple. We're not going to reiterate them all here, but they include social recovery, MFA, batching, session keys, sponsored transactions, fee payment in kind, supporting key schemes from different realms, etc. Read the articles from [Argent](https://www.argent.xyz/blog/part-3-wtf-is-account-abstraction/) or [Ethereum.org](https://ethereum.org/en/roadmap/account-abstraction/) for more detailed info. +## Aztec Account Abstraction -### Implementing at protocol vs application layer +### Authorization abstraction and DoS attacks -Instead of implementing it at the protocol level as in Aztec, account abstraction can be implemented at the application layer of a network using smart accounts and meta-transactions. When implementing account abstraction on Ethereum, the transaction being sent to the network is still an Ethereum transaction, but its payload is interpreted as a "transaction execution request" that is validated and run by the smart contract wallet. +While we talk about “arbitrary verification logic” describing the intuition behind AA, the logic is usually not really arbitrary. The verification logic (i.e. what can be checked as an authorization) is limited to make the verification time fast and bounded. If it is not bounded, an attacker can flood the mempool with expensive invalid transactions clogging the network. That is the case for all chains where transaction validity is checked by the sequencer. -A simple example would be Gnosis Safe (see [_Account Abstraction is NOT coming_](https://safe.mirror.xyz/9KmZjEbFkmI79s28d9xar6JWYrE50F5AHpa5CR12YGI)), where it's the multisig contract responsibility to define when an execution request is valid by checking it carries N out of M signatures, and then executing it. [Argent](https://www.argent.xyz/blog/wtf-is-account-abstraction/) has also been working on smart wallets for years, and collaborating with network teams to implement AA natively at the protocol layer. +On Aztec, there is no limitation on verification logic, as transaction validity check is executed client-side and a proof of validity is supplied to the sequencer. The sequencer only verifies the proof and this process is independent of the verification logic complexity. -Ethereum is currently following this approach via [EIP4337](https://eips.ethereum.org/EIPS/eip-4337), an evolution of the [GSN](https://opengsn.org/). This EIP defines a standard method for relaying meta-transactions in a decentralized way, including options for delegating payment to other agents (called paymasters). See [this chart](https://x.com/koeppelmann/status/1632257610455089154) on how 4337 relates to other smart contract wallet efforts. +This unlocks a whole universe of new use cases and optimization of existing ones. Whenever the dapp can benefit from moving expensive computations off-chain, Aztec AA will provide a unique chance for an optimization. That is to say, on traditional chains users pay for each executed opcode, hence more complex operations (e.g. alternative signature verification) are quite expensive. In the case of Aztec, it can be moved off-chain so that it becomes almost free. The user pays for the operations in terms of client-side prover time. -Implementing AA at the application layer has the main drawback that it's more complex than doing so at the protocol layer. It also leads to duplicated efforts in both layers (e.g. the wrapper transaction in a meta-transactions still needs to be checked for its ECDSA signature, and then the smart contract wallet needs to verify another set of signatures). -Now, there have also been multiple proposals for getting AA implemented at the _protocol_ level in Ethereum. This usually implies introducing a new transaction type or set of opcodes where signature verification and fee payment is handled by the EVM. See EIPs [2803](https://eips.ethereum.org/EIPS/eip-2803), [2938](https://eips.ethereum.org/EIPS/eip-2938), or [3074](https://eips.ethereum.org/EIPS/eip-3074). None of these have gained traction due to the efforts involved in implementing while keeping backwards compatibility. +Couple of examples: +- Multisig contract with an arbitrary number of parties that can verify any number of signatures for free. +- Oracle contract with an arbitrary number of data providers that can verify any number of data entries for free. -However, other chains are experimenting with protocol-level AA. Both [Starknet](https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/introduction/#account_abstraction) and [zkSync](https://docs.zksync.io/build/developer-reference/account-abstraction) have native AA, zkSync being the first EVM-compatible one to do so. To maintain Ethereum compatibility, zkSync implements a [default account contract](https://github.com/matter-labs/era-system-contracts/blob/main/contracts/DefaultAccount.sol) in Solidity that mimics Ethereum's protocol behavior. +## Aztec account -### Preventing DoS attacks +Aztec has no concept of EOAs (Externally Owned Accounts). Every account is implemented as a contract. -Protocol AA implementations are vulnerable to DoS attacks due to the unrestricted cost of validating a transaction. If validating a transaction requires custom logic that can be arbitrarily expensive, an attacker can flood the mempool with these transactions that block builders cannot differentiate from legit ones. +### Entrypoints -Application AA implementations face a similar issue: a smart wallet could return that a transaction is valid when a relayer is about to submit it on-chain and pay for its gas, but when the transaction is actually mined it could turn invalid. +Account contracts usually have a specific function called `entrypoint`. It serves as the interface for interaction with the smart contract and can be called by external users or other smart contracts. -All implementations mitigate these issues by restricting what's doable in the validation phase. EIP4337 defines a set of prohibited opcodes and limits storage access (see [Simulation](https://eips.ethereum.org/EIPS/eip-4337#simulation) in the EIP), and requires a [reputation system](https://eips.ethereum.org/EIPS/eip-4337#reputation-scoring-and-throttlingbanning-for-global-entities) for global entities. zkSync [relaxes](https://docs.zksync.io/build/developer-reference/account-abstraction/extending-4337) opcode requirements a bit, and Starknet simply [does not allow to call external contracts](https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/validate_and_execute/). - -## Accounts in Aztec - -Aztec has no concept of Externally Owned Accounts. Every account is implemented as a contract. Account contracts typically implement an `entrypoint` function that receives the actions to be carried out and an authentication payload. In pseudocode: +An `entrypoint` function receives the actions to be carried out and an authentication payload. In pseudocode: ``` publicKey: PublicKey; @@ -74,64 +66,69 @@ def entryPoint(payload): Read more about how to write an account contract [here](../../../tutorials/codealong/contract_tutorials/write_accounts_contract.md). -### Account contracts and wallets +A request for executing an action requires: +- The `origin` contract to execute as the first step. +- The initial function to call (usually `entrypoint`). +- The arguments (which encode the private and public calls to run as well as any signatures). -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 (e.g. "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. +Read more about how to write an account contract [here](../../../developers/tutorials/codealong/contract_tutorials/write_accounts_contract.md). -### Execution requests +### Non-standard entrypoints -Note that nothing related to signature verification or payload execution is enshrined in the protocol, since account contracts are free to define this entrypoint however they see fit. Therefore, a request for executing an action has a simpler structure than in Ethereum, and just requires: +Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application one. This allows implementing functions that do not need to be called by any particular user and are just intended to advance the state of a contract. -- The `origin` contract to execute as the first step -- The initial function to call (usually `entrypoint`) -- The arguments (which encode the private and public calls to run as well as any signatures) +For example, a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as `entrypoint` function. -### Entrypoint restrictions +For an example of this behavior see our [non_contract_account test](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation. Notice that the Signerless wallet doesn't invoke an `entrypoint` function of an account contract but instead invokes the target contract function directly. + +:::info +Entrypoints for the following cases: -Entrypoint methods are not enshrined in the protocol, and any function can be called as an entrypoint. The only restriction is that it must be private (not open), so all transactions are initiated with a client-side zero-knowledge proof. +- If no contract `entrypoint` is used `msg_sender` is set to `Field.max`. +- In a private to public `entrypoint`, `msg_sender` is the contract making the private to public call. +- When calling the `entrypoint` on an account contract, `msg_sender` is set to the account contract address. +::: -This means that, unlike other protocols, Aztec does not impose any restrictions on the actions that can be carried out during the validation phase of the entrypoint, since these actions are executed by the client and wrapped in a zero-knowledge proof that is verified by the network. You are free to call into other contracts, access storage, or do as much computing as you need in the entrypoint. +### Account contracts and wallets -### Nonces and replay protection +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 (e.g. "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. -Every transaction execution considered valid by the protocol emits the hash of the transaction execution request as a nullifier, preventing the same transaction from being executed more than once. Nonces, on the other hand, are left to the account contract implementation. This allows building accounts with strictly incremental nonces or where transactions can be processed out-of-order. +### Account initialization -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. +When a user wants to interact with the network, as their first action, they need to deploy their account contract (analogous to setting up a wallet with a seed phrase in an EOA). -### Non-standard entrypoints +However, this is not required when sitting on the receiving end. The user address can be deterministically derived from their encryption public key and the account contract they intend to deploy. So that the funds can be sent to an account that hasn’t been deployed yet. -Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application one in the protocol. This means that a transaction can be initiated in any contract. This allows implementing functions that do not need to be called by any particular user and are just intended to advance the state of a contract. +Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending fee juice to their account contract address (which can be derived deterministically, as mentioned above), so that the account has funds to pay for its own deployment. Alternatively, the fee can be paid for by another account, using [fee abstraction](#fee-abstraction). -As an example, we can think of a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as entrypoint function. For an example of this behavior see our [non_contract_account test](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation. -Notice that the Signerless wallet doesn't invoke an entrypoint function of an account contract but instead invokes the target contract function directly. +## What is account address -:::info -Entrypoints for the following cases: +Address is derived from the [address keys](keys.md#address-keys). While the AddressPublicKey is an elliptic curve point of the form (x,y), the address is its x coordinate. -- if no contract entrypoint is used `msg_sender` is set to `Field.max`. -- in a private to public entrypoint, `msg_sender` is the contract making the private to public call -- when calling the entrypoint on an account contract, `msg_sender` is set to the account contract address - ::: +The address y coordinate can be derived when needed using the formula y = sqrt(x^3 - 17), coming from the [Grumpkin elliptic curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md). For x to be legit as an address there should exist a corresponding y that satisfies the aforementioned equation: any field element cannot work as an address. -### Account initialization +### Complete address -The protocol requires that every account is a contract for the purposes of sending a transaction. This means that a user needs to deploy their account contract as their first action when they want to interact with the network. +Because of the contract address derivation scheme you can check that a given set of public [keys](keys.md) corresponds to a given address just by trying to recompute it. -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. +You can check that a given set of public keys corresponds to a given address by recomputing it, thanks to the contract address derivation scheme. -### Account contract deployment +Since this is commonly needed when sending a [note](../concepts/storage/notes.md) to an account, we coined the term complete address for the collection of: +- all the user's public keys, +- [partial address](keys.md#address-keys), +- contract address. -Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending Fee Juice 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). +Once the complete address is shared with the sender, the sender can use the public keys and partial address to check that the complete address is correct, and then send the notes to that address. -### Authorizing actions +### 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. -When executing a private function, this authorization is checked by requesting an _auth witness_ from the execution oracle, which is usually a signed message. -The user's Private eXecution Environment (PXE) is responsible for storing these auth witnesses and returning them to the requesting account contract. -Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it out-of-band. +When executing a private function, this authorization is checked by requesting an authentication witness from the execution oracle, which is usually a signed message. Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties (e.g. protocols or other users) to execute an action on their behalf. -However, during a public function execution, it is not possible to retrieve a value from the local oracle. To support authorizations in public functions, account contracts should save in contract storage what actions have been pre-authorized by their owner. +The user's [Private eXecution Environment (PXE)](../concepts/pxe/index.md) is responsible for storing these auth witnesses and returning them to the requesting account contract. Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it out-of-band. + +However, during a public function execution, it is not possible to retrieve a value from the local [oracle](../../smart_contracts/oracles/index.md). To support authorizations in public functions, account contracts should save in contract storage what actions have been pre-authorized by their owner. These two patterns combined allow an account contract to answer whether an action `is_valid_impl` for a given user both in private and public contexts. @@ -143,16 +140,38 @@ Transaction simulations in the PXE are not currently simulated, this is future w ::: -### Encryption and nullifying keys +## Nonce and fee abstraction + +Beyond the authentication logic abstraction, there are nonce abstraction and fee abstraction. + +### Nonce abstraction + +Nonce is a sequentially increasing number (transaction-over-transaction) and it is utilized for replay protection (i.e. preventing users from executing a transaction more than once and unauthorized reordering). + +In particular, nonce management defines what it means for a transaction to be canceled, the rules of transaction ordering, and replay protection. In Ethereum, nonce is enshrined into the protocol. For example, to cancel a transaction, its sender should send another transaction with the same nonce but with a higher transaction fee. + +On the Aztec network, nonce is abstracted i.e. if a developer wants to customize it, they get to decide how they handle replay protection, transaction cancellation, as well as ordering. It’s mostly relevant to those building wallets. For example, a developer can design a wallet that allows sending big transactions with very low priority fees because the transactions are not time sensitive (i.e. the preference is that a transaction is cheap and doesn’t matter if it is slow). If one tries to apply this logic today on Ethereum (under sequential nonces), when they send a large, slow transaction they can’t send any other transactions until that first large, slow transaction is processed. + +### Fee abstraction + +It doesn’t have to be the transaction sender who pays the transaction fees. Wallets or dapp developers can choose any payment logic they want using a paymaster. + +Paymaster is a contract that can pay for transactions on behalf of users. +- It can be managed by a dapp itself (e.g. a DEX can have its own paymaster) or operate as a third party service available for everyone. +- Fees can be paid publicly or privately. +- Fees can be paid in any token. + +Fee abstraction unlocks use cases like: +- Sponsored transactions (e.g. the dapp’s business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. +- Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token). + + + + -Aztec requires users to define [encryption and nullifying keys](./keys.md) that are needed for receiving and spending private notes. Unlike transaction signing, encryption and nullifying is enshrined at the protocol. This means that there is a single scheme used for encryption and nullifying. These keys are derived from a master public key. This master public key, in turn, is used when deterministically deriving the account's address. A side effect of committing to a master public key as part of the address is that _this key cannot be rotated_. While an account contract implementation could include methods for rotating the signing key, this is unfortunately not possible for encryption and nullifying keys (note that rotating nullifying keys also creates other challenges such as preventing double spends). We are exploring usage of [`SharedMutable`](../../../reference/developer_references/smart_contract_reference/storage/shared_state.md#sharedmutable) to enable rotating these keys. -NOTE: While we entertained the idea of abstracting note encryption, where account contracts would define an `encrypt` method that would use a user-defined scheme, there are two main reasons we decided against this. First is that this entailed that, in order to receive funds, a user had to first deploy their account contract, which is a major UX issue. Second, users could define malicious `encrypt` methods that failed in certain circumstances, breaking application flows that required them to receive a private note. While this issue already exists in Ethereum when transferring ETH (see the [king of the hill](https://coinsbench.com/27-king-ethernaut-da5021cd4aa6)), its impact is made worse in Aztec since any execution failure in a private function makes the entire transaction unprovable (ie it is not possible to catch errors in calls to other private functions), and furthermore because encryption is required for any private state (not just for transferring ETH). Nevertheless, both of these problems are solvable. Initialization can be worked around by embedding a commitment to the bytecode in the address and removing the need for actually deploying contracts before interacting with them, and the king of the hill issue can be mitigated by introducing a full private VM that allows catching reverts. As such, we may be able to abstract encryption in the future as well. -### Fee management -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. From dcd54167918a15963c4d6aa6833d19f7c7d3e828 Mon Sep 17 00:00:00 2001 From: Lisa Date: Thu, 13 Feb 2025 21:18:13 +0200 Subject: [PATCH 14/18] Accounts page update --- docs/docs/aztec/concepts/accounts/index.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/index.md b/docs/docs/aztec/concepts/accounts/index.md index 21228313369..eed7bb68ec2 100644 --- a/docs/docs/aztec/concepts/accounts/index.md +++ b/docs/docs/aztec/concepts/accounts/index.md @@ -163,15 +163,4 @@ Paymaster is a contract that can pay for transactions on behalf of users. Fee abstraction unlocks use cases like: - Sponsored transactions (e.g. the dapp’s business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. -- Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token). - - - - - - -A side effect of committing to a master public key as part of the address is that _this key cannot be rotated_. While an account contract implementation could include methods for rotating the signing key, this is unfortunately not possible for encryption and nullifying keys (note that rotating nullifying keys also creates other challenges such as preventing double spends). We are exploring usage of [`SharedMutable`](../../../reference/developer_references/smart_contract_reference/storage/shared_state.md#sharedmutable) to enable rotating these keys. - - - - +- Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token). \ No newline at end of file From dd7f5999c545228d0669536b4934667204ab6d57 Mon Sep 17 00:00:00 2001 From: Lisa Date: Thu, 13 Feb 2025 21:28:04 +0200 Subject: [PATCH 15/18] Trying to fix the accounts page --- docs/docs/aztec/concepts/accounts/index.md | 163 +-------------------- 1 file changed, 1 insertion(+), 162 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/index.md b/docs/docs/aztec/concepts/accounts/index.md index eed7bb68ec2..b0a7f881d00 100644 --- a/docs/docs/aztec/concepts/accounts/index.md +++ b/docs/docs/aztec/concepts/accounts/index.md @@ -2,165 +2,4 @@ title: Accounts sidebar_position: 1 tags: [accounts] ---- - -Aztec has native account abstraction. Every account in Aztec is a smart contract. - -In this section, you’ll learn about Aztec’s account abstraction, Aztec accounts and address derivation, how wallets relate to accounts, and how the entrypoints are defined. - -## Account Abstraction (AA) - -Under the Account Abstraction, the identity of a user is usually represented by a smart contract. - -Among the account parts to be abstracted are authentication (“Who I am”), authorization (“What I am allowed to do”), replay protection, fee payment, and execution. - -Among the use cases to be unlocked by Account Abstraction are account recovery, gas sponsorship, multichain accounts, and support of signatures other than ECDSA, such as more efficient signatures (e.g. Schnorr, BLS), or more user-friendly ones (e.g. smartphone secure enclave). - -### Protocol vs application level - -AA can be implemented at the protocol level, then we call it native Account Abstraction. In this case, all the accounts on the network are smart contracts. AA can also be implemented at the smart-contract level, then we call it non-native Account Abstraction. In this case, there might be both EOAs and accounts controlled by smart contracts. - -In the case of Aztec, we have native Account Abstraction. - -## Aztec Account Abstraction - -### Authorization abstraction and DoS attacks - -While we talk about “arbitrary verification logic” describing the intuition behind AA, the logic is usually not really arbitrary. The verification logic (i.e. what can be checked as an authorization) is limited to make the verification time fast and bounded. If it is not bounded, an attacker can flood the mempool with expensive invalid transactions clogging the network. That is the case for all chains where transaction validity is checked by the sequencer. - -On Aztec, there is no limitation on verification logic, as transaction validity check is executed client-side and a proof of validity is supplied to the sequencer. The sequencer only verifies the proof and this process is independent of the verification logic complexity. - -This unlocks a whole universe of new use cases and optimization of existing ones. Whenever the dapp can benefit from moving expensive computations off-chain, Aztec AA will provide a unique chance for an optimization. That is to say, on traditional chains users pay for each executed opcode, hence more complex operations (e.g. alternative signature verification) are quite expensive. In the case of Aztec, it can be moved off-chain so that it becomes almost free. The user pays for the operations in terms of client-side prover time. - - -Couple of examples: -- Multisig contract with an arbitrary number of parties that can verify any number of signatures for free. -- Oracle contract with an arbitrary number of data providers that can verify any number of data entries for free. - -## Aztec account - -Aztec has no concept of EOAs (Externally Owned Accounts). Every account is implemented as a contract. - -### Entrypoints - -Account contracts usually have a specific function called `entrypoint`. It serves as the interface for interaction with the smart contract and can be called by external users or other smart contracts. - -An `entrypoint` function receives the actions to be carried out and an authentication payload. In pseudocode: - -``` -publicKey: PublicKey; - -def entryPoint(payload): - let { privateCalls, publicCalls, nonce, signature } = payload; - let payloadHash = hash(privateCalls, publicCalls, nonce); - validateSignature(this.publicKey, signature, payloadHash); - - foreach privateCall in privateCalls: - let { to, data, value } = privateCall; - call(to, data, value); - - foreach publicCall in publicCalls: - let { to, data, value, gasLimit } = publicCall; - enqueueCall(to, data, value, gasLimit); -``` - -Read more about how to write an account contract [here](../../../tutorials/codealong/contract_tutorials/write_accounts_contract.md). - -A request for executing an action requires: -- The `origin` contract to execute as the first step. -- The initial function to call (usually `entrypoint`). -- The arguments (which encode the private and public calls to run as well as any signatures). - -Read more about how to write an account contract [here](../../../developers/tutorials/codealong/contract_tutorials/write_accounts_contract.md). - -### Non-standard entrypoints - -Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application one. This allows implementing functions that do not need to be called by any particular user and are just intended to advance the state of a contract. - -For example, a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as `entrypoint` function. - -For an example of this behavior see our [non_contract_account test](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation. Notice that the Signerless wallet doesn't invoke an `entrypoint` function of an account contract but instead invokes the target contract function directly. - -:::info -Entrypoints for the following cases: - -- If no contract `entrypoint` is used `msg_sender` is set to `Field.max`. -- In a private to public `entrypoint`, `msg_sender` is the contract making the private to public call. -- When calling the `entrypoint` on an account contract, `msg_sender` is set to the account contract address. -::: - -### 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 (e.g. "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. - -### Account initialization - -When a user wants to interact with the network, as their first action, they need to deploy their account contract (analogous to setting up a wallet with a seed phrase in an EOA). - -However, this is not required when sitting on the receiving end. The user address can be deterministically derived from their encryption public key and the account contract they intend to deploy. So that the funds can be sent to an account that hasn’t been deployed yet. - -Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending fee juice to their account contract address (which can be derived deterministically, as mentioned above), so that the account has funds to pay for its own deployment. Alternatively, the fee can be paid for by another account, using [fee abstraction](#fee-abstraction). - -## What is account address - -Address is derived from the [address keys](keys.md#address-keys). While the AddressPublicKey is an elliptic curve point of the form (x,y), the address is its x coordinate. - -The address y coordinate can be derived when needed using the formula y = sqrt(x^3 - 17), coming from the [Grumpkin elliptic curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md). For x to be legit as an address there should exist a corresponding y that satisfies the aforementioned equation: any field element cannot work as an address. - -### Complete address - -Because of the contract address derivation scheme you can check that a given set of public [keys](keys.md) corresponds to a given address just by trying to recompute it. - -You can check that a given set of public keys corresponds to a given address by recomputing it, thanks to the contract address derivation scheme. - -Since this is commonly needed when sending a [note](../concepts/storage/notes.md) to an account, we coined the term complete address for the collection of: -- all the user's public keys, -- [partial address](keys.md#address-keys), -- contract address. - -Once the complete address is shared with the sender, the sender can use the public keys and partial address to check that the complete address is correct, and then send the notes to that address. - -### 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. - -When executing a private function, this authorization is checked by requesting an authentication witness from the execution oracle, which is usually a signed message. Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties (e.g. protocols or other users) to execute an action on their behalf. - -The user's [Private eXecution Environment (PXE)](../concepts/pxe/index.md) is responsible for storing these auth witnesses and returning them to the requesting account contract. Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it out-of-band. - -However, during a public function execution, it is not possible to retrieve a value from the local [oracle](../../smart_contracts/oracles/index.md). To support authorizations in public functions, account contracts should save in contract storage what actions have been pre-authorized by their owner. - -These two patterns combined allow an account contract to answer whether an action `is_valid_impl` for a given user both in private and public contexts. - -You can read more about authorizing actions with authorization witnesses on [this page](./authwit.md). - -:::info - -Transaction simulations in the PXE are not currently simulated, this is future work described [here](https://github.com/AztecProtocol/aztec-packages/issues/9133). This means that any transaction simulations that call into a function requiring an authwit will require the user to provide an authwit. Without simulating simulations, the PXE can't anticipate what authwits a transaction may need, so developers will need to manually request these authwits from users. In the future, transactions requiring authwits will be smart enough to ask the user for the correct authwits automatically. - -::: - -## Nonce and fee abstraction - -Beyond the authentication logic abstraction, there are nonce abstraction and fee abstraction. - -### Nonce abstraction - -Nonce is a sequentially increasing number (transaction-over-transaction) and it is utilized for replay protection (i.e. preventing users from executing a transaction more than once and unauthorized reordering). - -In particular, nonce management defines what it means for a transaction to be canceled, the rules of transaction ordering, and replay protection. In Ethereum, nonce is enshrined into the protocol. For example, to cancel a transaction, its sender should send another transaction with the same nonce but with a higher transaction fee. - -On the Aztec network, nonce is abstracted i.e. if a developer wants to customize it, they get to decide how they handle replay protection, transaction cancellation, as well as ordering. It’s mostly relevant to those building wallets. For example, a developer can design a wallet that allows sending big transactions with very low priority fees because the transactions are not time sensitive (i.e. the preference is that a transaction is cheap and doesn’t matter if it is slow). If one tries to apply this logic today on Ethereum (under sequential nonces), when they send a large, slow transaction they can’t send any other transactions until that first large, slow transaction is processed. - -### Fee abstraction - -It doesn’t have to be the transaction sender who pays the transaction fees. Wallets or dapp developers can choose any payment logic they want using a paymaster. - -Paymaster is a contract that can pay for transactions on behalf of users. -- It can be managed by a dapp itself (e.g. a DEX can have its own paymaster) or operate as a third party service available for everyone. -- Fees can be paid publicly or privately. -- Fees can be paid in any token. - -Fee abstraction unlocks use cases like: -- Sponsored transactions (e.g. the dapp’s business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. -- Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token). \ No newline at end of file +--- \ No newline at end of file From e0d0ec4124eb2d7d03a82e5e3ade62638a31ac03 Mon Sep 17 00:00:00 2001 From: Lisa Date: Fri, 14 Feb 2025 20:01:04 +0200 Subject: [PATCH 16/18] Updating accounts page --- docs/docs/aztec/concepts/accounts/index.md | 150 +++++++++++---------- 1 file changed, 79 insertions(+), 71 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/index.md b/docs/docs/aztec/concepts/accounts/index.md index bfd8d6a2971..210f9e01aa8 100644 --- a/docs/docs/aztec/concepts/accounts/index.md +++ b/docs/docs/aztec/concepts/accounts/index.md @@ -4,56 +4,48 @@ sidebar_position: 1 tags: [accounts] --- -**Every account in Aztec is a smart contract** which defines the rules for whether a transaction is or is not valid. This allows implementing different schemes for transaction signing, nonce management, and fee payments. However, encryption and nullifying keys, which are specific to private blockchains, are still enshrined at the protocol level. +Aztec has native account abstraction. Every account in Aztec is a smart contract. -In this section, you’ll learn about how Aztec defines AA (account abstraction) and its correlation with encryption keys and nullifying keys. We’ll go through: +In this section, you’ll learn about Aztec’s account abstraction, Aztec accounts and address derivation, how wallets relate to accounts, and how the entrypoints are defined. -- The importance and implications of AA -- Understanding account contracts and wallets in relation to Aztec -- Concept of authorization and actions along with encryption -- The future of fee management in Aztec +## Account Abstraction (AA) -## Background +Under the Account Abstraction, the identity of a user is usually represented by a smart contract. -We'll start with the mandatory "what is AA" section that every single article on the topic has, so you can skip this if you're familiar with the topic. +Among the account parts to be abstracted are authentication (“Who I am”), authorization (“What I am allowed to do”), replay protection, fee payment, and execution. -### What is account abstraction? +Among the use cases to be unlocked by Account Abstraction are account recovery, gas sponsorship, multichain accounts, and support of signatures other than ECDSA, such as more efficient signatures (e.g. Schnorr, BLS), or more user-friendly ones (e.g. smartphone secure enclave). -We'll refer to AA as the _ability to set the validity conditions of a transaction programmatically_ ([source](https://fuel-labs.ghost.io/account-abstraction-for-everyone-else/)). [Starknet](https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/introduction/#account_abstraction) goes one step further and splits AA into three different components: +### Protocol vs application level -- Signature abstraction (defining when a signature is accepted) -- Fee abstraction (paying fees) -- Nonce abstraction (replay protection and ordering) +AA can be implemented at the protocol level, then we call it native Account Abstraction. In this case, all the accounts on the network are smart contracts. AA can also be implemented at the smart-contract level, then we call it non-native Account Abstraction. In this case, there might be both EOAs and accounts controlled by smart contracts. -In most AA schemes, the identity of a user is no longer represented by a keypair but by a contract, often called a smart contract wallet or account contract. This contract receives transaction payloads which are validated with custom logic, and then interpreted as actions to execute, like calling into another contract. +In the case of Aztec, we have native Account Abstraction. -The benefits of AA are multiple. We're not going to reiterate them all here, but they include social recovery, MFA, batching, session keys, sponsored transactions, fee payment in kind, supporting key schemes from different realms, etc. Read the articles from [Argent](https://www.argent.xyz/blog/part-3-wtf-is-account-abstraction/) or [Ethereum.org](https://ethereum.org/en/roadmap/account-abstraction/) for more detailed info. +## Aztec Account Abstraction -### Implementing at protocol vs application layer +### Authorization abstraction and DoS attacks -Instead of implementing it at the protocol level as in Aztec, account abstraction can be implemented at the application layer of a network using smart accounts and meta-transactions. When implementing account abstraction on Ethereum, the transaction being sent to the network is still an Ethereum transaction, but its payload is interpreted as a "transaction execution request" that is validated and run by the smart contract wallet. +While we talk about “arbitrary verification logic” describing the intuition behind AA, the logic is usually not really arbitrary. The verification logic (i.e. what can be checked as an authorization) is limited to make the verification time fast and bounded. If it is not bounded, an attacker can flood the mempool with expensive invalid transactions clogging the network. That is the case for all chains where transaction validity is checked by the sequencer. -A simple example would be Gnosis Safe (see [_Account Abstraction is NOT coming_](https://safe.mirror.xyz/9KmZjEbFkmI79s28d9xar6JWYrE50F5AHpa5CR12YGI)), where it's the multisig contract responsibility to define when an execution request is valid by checking it carries N out of M signatures, and then executing it. [Argent](https://www.argent.xyz/blog/wtf-is-account-abstraction/) has also been working on smart wallets for years, and collaborating with network teams to implement AA natively at the protocol layer. +On Aztec, there is no limitation on verification logic, as transaction validity check is executed client-side and a proof of validity is supplied to the sequencer. The sequencer only verifies the proof and this process is independent of the verification logic complexity. -Ethereum is currently following this approach via [EIP4337](https://eips.ethereum.org/EIPS/eip-4337), an evolution of the [GSN](https://opengsn.org/). This EIP defines a standard method for relaying meta-transactions in a decentralized way, including options for delegating payment to other agents (called paymasters). See [this chart](https://x.com/koeppelmann/status/1632257610455089154) on how 4337 relates to other smart contract wallet efforts. +This unlocks a whole universe of new use cases and optimization of existing ones. Whenever the dapp can benefit from moving expensive computations off-chain, Aztec AA will provide a unique chance for an optimization. That is to say, on traditional chains users pay for each executed opcode, hence more complex operations (e.g. alternative signature verification) are quite expensive. In the case of Aztec, it can be moved off-chain so that it becomes almost free. The user pays for the operations in terms of client-side prover time. -Implementing AA at the application layer has the main drawback that it's more complex than doing so at the protocol layer. It also leads to duplicated efforts in both layers (e.g. the wrapper transaction in a meta-transactions still needs to be checked for its ECDSA signature, and then the smart contract wallet needs to verify another set of signatures). -Now, there have also been multiple proposals for getting AA implemented at the _protocol_ level in Ethereum. This usually implies introducing a new transaction type or set of opcodes where signature verification and fee payment is handled by the EVM. See EIPs [2803](https://eips.ethereum.org/EIPS/eip-2803), [2938](https://eips.ethereum.org/EIPS/eip-2938), or [3074](https://eips.ethereum.org/EIPS/eip-3074). None of these have gained traction due to the efforts involved in implementing while keeping backwards compatibility. +Couple of examples: +- Multisig contract with an arbitrary number of parties that can verify any number of signatures for free. +- Oracle contract with an arbitrary number of data providers that can verify any number of data entries for free. -However, other chains are experimenting with protocol-level AA. Both [Starknet](https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/introduction/#account_abstraction) and [zkSync](https://docs.zksync.io/build/developer-reference/account-abstraction) have native AA, zkSync being the first EVM-compatible one to do so. To maintain Ethereum compatibility, zkSync implements a [default account contract](https://github.com/matter-labs/era-system-contracts/blob/main/contracts/DefaultAccount.sol) in Solidity that mimics Ethereum's protocol behavior. +## Aztec account -### Preventing DoS attacks +Aztec has no concept of EOAs (Externally Owned Accounts). Every account is implemented as a contract. -Protocol AA implementations are vulnerable to DoS attacks due to the unrestricted cost of validating a transaction. If validating a transaction requires custom logic that can be arbitrarily expensive, an attacker can flood the mempool with these transactions that block builders cannot differentiate from legit ones. +### Entrypoints -Application AA implementations face a similar issue: a smart wallet could return that a transaction is valid when a relayer is about to submit it on-chain and pay for its gas, but when the transaction is actually mined it could turn invalid. +Account contracts usually have a specific function called `entrypoint`. It serves as the interface for interaction with the smart contract and can be called by external users or other smart contracts. -All implementations mitigate these issues by restricting what's doable in the validation phase. EIP4337 defines a set of prohibited opcodes and limits storage access (see [Simulation](https://eips.ethereum.org/EIPS/eip-4337#simulation) in the EIP), and requires a [reputation system](https://eips.ethereum.org/EIPS/eip-4337#reputation-scoring-and-throttlingbanning-for-global-entities) for global entities. zkSync [relaxes](https://docs.zksync.io/build/developer-reference/account-abstraction/extending-4337) opcode requirements a bit, and Starknet simply [does not allow to call external contracts](https://docs.starknet.io/documentation/architecture_and_concepts/Accounts/validate_and_execute/). - -## Accounts in Aztec - -Aztec has no concept of Externally Owned Accounts. Every account is implemented as a contract. Account contracts typically implement an `entrypoint` function that receives the actions to be carried out and an authentication payload. In pseudocode: +An `entrypoint` function receives the actions to be carried out and an authentication payload. In pseudocode: ``` publicKey: PublicKey; @@ -72,70 +64,75 @@ def entryPoint(payload): enqueueCall(to, data, value, gasLimit); ``` -Read more about how to write an account contract [here](../../../developers/tutorials/codealong/contract_tutorials/write_accounts_contract.md). +Read more about how to write an account contract [here](../../../tutorials/codealong/contract_tutorials/write_accounts_contract.md). -### Account contracts and wallets +A request for executing an action requires: +- The `origin` contract to execute as the first step. +- The initial function to call (usually `entrypoint`). +- The arguments (which encode the private and public calls to run as well as any signatures). -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 (e.g. "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. +Read more about how to write an account contract [here](../../../developers/tutorials/codealong/contract_tutorials/write_accounts_contract.md). -### Execution requests +### Non-standard entrypoints -Note that nothing related to signature verification or payload execution is enshrined in the protocol, since account contracts are free to define this entrypoint however they see fit. Therefore, a request for executing an action has a simpler structure than in Ethereum, and just requires: +Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application one. This allows implementing functions that do not need to be called by any particular user and are just intended to advance the state of a contract. -- The `origin` contract to execute as the first step -- The initial function to call (usually `entrypoint`) -- The arguments (which encode the private and public calls to run as well as any signatures) +For example, a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as `entrypoint` function. -### Entrypoint restrictions +For an example of this behavior see our [non_contract_account test](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation. Notice that the Signerless wallet doesn't invoke an `entrypoint` function of an account contract but instead invokes the target contract function directly. -Entrypoint methods are not enshrined in the protocol, and any function can be called as an entrypoint. The only restriction is that it must be private (not open), so all transactions are initiated with a client-side zero-knowledge proof. +:::info +Entrypoints for the following cases: + +- If no contract `entrypoint` is used `msg_sender` is set to `Field.max`. +- In a private to public `entrypoint`, `msg_sender` is the contract making the private to public call. +- When calling the `entrypoint` on an account contract, `msg_sender` is set to the account contract address. +::: -This means that, unlike other protocols, Aztec does not impose any restrictions on the actions that can be carried out during the validation phase of the entrypoint, since these actions are executed by the client and wrapped in a zero-knowledge proof that is verified by the network. You are free to call into other contracts, access storage, or do as much computing as you need in the entrypoint. +### Account contracts and wallets -### Nonces and replay protection +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 (e.g. "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. -Every transaction execution considered valid by the protocol emits the hash of the transaction execution request as a nullifier, preventing the same transaction from being executed more than once. Nonces, on the other hand, are left to the account contract implementation. This allows building accounts with strictly incremental nonces or where transactions can be processed out-of-order. +### Account initialization -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. +When a user wants to interact with the network, as their first action, they need to deploy their account contract (analogous to setting up a wallet with a seed phrase in an EOA). -### Non-standard entrypoints +However, this is not required when sitting on the receiving end. The user address can be deterministically derived from their encryption public key and the account contract they intend to deploy. So that the funds can be sent to an account that hasn’t been deployed yet. -Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application one in the protocol. This means that a transaction can be initiated in any contract. This allows implementing functions that do not need to be called by any particular user and are just intended to advance the state of a contract. +Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending fee juice to their account contract address (which can be derived deterministically, as mentioned above), so that the account has funds to pay for its own deployment. Alternatively, the fee can be paid for by another account, using [fee abstraction](#fee-abstraction). -As an example, we can think of a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as entrypoint function. For an example of this behavior see our [non_contract_account test](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation. -Notice that the Signerless wallet doesn't invoke an entrypoint function of an account contract but instead invokes the target contract function directly. +## What is account address -:::info -Entrypoints for the following cases: +Address is derived from the [address keys](keys.md#address-keys). While the AddressPublicKey is an elliptic curve point of the form (x,y), the address is its x coordinate. -- if no contract entrypoint is used `msg_sender` is set to `Field.max`. -- in a private to public entrypoint, `msg_sender` is the contract making the private to public call -- when calling the entrypoint on an account contract, `msg_sender` is set to the account contract address - ::: +The address y coordinate can be derived when needed using the formula y = sqrt(x^3 - 17), coming from the [Grumpkin elliptic curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md). For x to be legit as an address there should exist a corresponding y that satisfies the aforementioned equation: any field element cannot work as an address. -### Account initialization +### Complete address -The protocol requires that every account is a contract for the purposes of sending a transaction. This means that a user needs to deploy their account contract as their first action when they want to interact with the network. +Because of the contract address derivation scheme you can check that a given set of public [keys](keys.md) corresponds to a given address just by trying to recompute it. -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. +You can check that a given set of public keys corresponds to a given address by recomputing it, thanks to the contract address derivation scheme. -### Account contract deployment +Since this is commonly needed when sending a [note](../concepts/storage/notes.md) to an account, we coined the term complete address for the collection of: +- all the user's public keys, +- [partial address](keys.md#address-keys), +- contract address. -Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending Fee Juice 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). +Once the complete address is shared with the sender, the sender can use the public keys and partial address to check that the complete address is correct, and then send the notes to that address. ### 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. -When executing a private function, this authorization is checked by requesting an _auth witness_ from the execution oracle, which is usually a signed message. -The user's Private eXecution Environment (PXE) is responsible for storing these auth witnesses and returning them to the requesting account contract. -Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it out-of-band. +When executing a private function, this authorization is checked by requesting an authentication witness from the execution oracle, which is usually a signed message. Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties (e.g. protocols or other users) to execute an action on their behalf. + +The user's [Private eXecution Environment (PXE)](../concepts/pxe/index.md) is responsible for storing these auth witnesses and returning them to the requesting account contract. Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it out-of-band. -However, during a public function execution, it is not possible to retrieve a value from the local oracle. To support authorizations in public functions, account contracts should save in contract storage what actions have been pre-authorized by their owner. +However, during a public function execution, it is not possible to retrieve a value from the local [oracle](../../smart_contracts/oracles/index.md). To support authorizations in public functions, account contracts should save in contract storage what actions have been pre-authorized by their owner. These two patterns combined allow an account contract to answer whether an action `is_valid_impl` for a given user both in private and public contexts. -You can read more about authorizing actions with authorization witnesses on [this page](../advanced/authwit.md). +You can read more about authorizing actions with authorization witnesses on [this page](./authwit.md). :::info @@ -143,16 +140,27 @@ Transaction simulations in the PXE are not currently simulated, this is future w ::: -### Encryption and nullifying keys +## Nonce and fee abstraction + +Beyond the authentication logic abstraction, there are nonce abstraction and fee abstraction. + +### Nonce abstraction + +Nonce is a sequentially increasing number (transaction-over-transaction) and it is utilized for replay protection (i.e. preventing users from executing a transaction more than once and unauthorized reordering). -Aztec requires users to define [encryption and nullifying keys](./keys.md) that are needed for receiving and spending private notes. Unlike transaction signing, encryption and nullifying is enshrined at the protocol. This means that there is a single scheme used for encryption and nullifying. These keys are derived from a master public key. This master public key, in turn, is used when deterministically deriving the account's address. +In particular, nonce management defines what it means for a transaction to be canceled, the rules of transaction ordering, and replay protection. In Ethereum, nonce is enshrined into the protocol. For example, to cancel a transaction, its sender should send another transaction with the same nonce but with a higher transaction fee. -A side effect of committing to a master public key as part of the address is that _this key cannot be rotated_. While an account contract implementation could include methods for rotating the signing key, this is unfortunately not possible for encryption and nullifying keys (note that rotating nullifying keys also creates other challenges such as preventing double spends). We are exploring usage of [`SharedMutable`](../../../developers/reference/smart_contract_reference/storage/shared_state.md) to enable rotating these keys. +On the Aztec network, nonce is abstracted i.e. if a developer wants to customize it, they get to decide how they handle replay protection, transaction cancellation, as well as ordering. It’s mostly relevant to those building wallets. For example, a developer can design a wallet that allows sending big transactions with very low priority fees because the transactions are not time sensitive (i.e. the preference is that a transaction is cheap and doesn’t matter if it is slow). If one tries to apply this logic today on Ethereum (under sequential nonces), when they send a large, slow transaction they can’t send any other transactions until that first large, slow transaction is processed. -NOTE: While we entertained the idea of abstracting note encryption, where account contracts would define an `encrypt` method that would use a user-defined scheme, there are two main reasons we decided against this. First is that this entailed that, in order to receive funds, a user had to first deploy their account contract, which is a major UX issue. Second, users could define malicious `encrypt` methods that failed in certain circumstances, breaking application flows that required them to receive a private note. While this issue already exists in Ethereum when transferring ETH (see the [king of the hill](https://coinsbench.com/27-king-ethernaut-da5021cd4aa6)), its impact is made worse in Aztec since any execution failure in a private function makes the entire transaction unprovable (ie it is not possible to catch errors in calls to other private functions), and furthermore because encryption is required for any private state (not just for transferring ETH). Nevertheless, both of these problems are solvable. Initialization can be worked around by embedding a commitment to the bytecode in the address and removing the need for actually deploying contracts before interacting with them, and the king of the hill issue can be mitigated by introducing a full private VM that allows catching reverts. As such, we may be able to abstract encryption in the future as well. +### Fee abstraction -### Fee management +It doesn’t have to be the transaction sender who pays the transaction fees. Wallets or dapp developers can choose any payment logic they want using a paymaster. -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. +Paymaster is a contract that can pay for transactions on behalf of users. +- It can be managed by a dapp itself (e.g. a DEX can have its own paymaster) or operate as a third party service available for everyone. +- Fees can be paid publicly or privately. +- Fees can be paid in any token. -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. +Fee abstraction unlocks use cases like: +- Sponsored transactions (e.g. the dapp’s business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. +- Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token). \ No newline at end of file From d2734410c37d983d3707ac9f53b1b4d563194a53 Mon Sep 17 00:00:00 2001 From: Lisa Date: Fri, 14 Feb 2025 21:27:06 +0200 Subject: [PATCH 17/18] Trying to update accounts page and resolve the popped up issues --- docs/docs/aztec/concepts/accounts/keys.md | 103 +----------------- .../schnorr_account_contract/src/main.nr | 10 +- 2 files changed, 3 insertions(+), 110 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/keys.md b/docs/docs/aztec/concepts/accounts/keys.md index f6a95578299..89b1687ddce 100644 --- a/docs/docs/aztec/concepts/accounts/keys.md +++ b/docs/docs/aztec/concepts/accounts/keys.md @@ -7,7 +7,6 @@ In this section, you will learn what keys are used in Aztec, and how the address ## Types of keys -<<<<<<< HEAD Each Aztec account is backed by four key pairs: - Nullifier keys – used to spend notes. @@ -55,59 +54,13 @@ When it comes to notes encryption and decryption: ### Signing keys -:diffg LOThanks to the native [account abstraction](../accounts/index.md), authorization logic can be implemented in an alternative way that is up to the developer (e.g. using Google authorization credentials, vanilla password logic or Face ID mechanism). In these cases, signing keys may not be relevant. -======= -Each Aztec account is backed by four key pairs: -- Nullifier keys – used to spend notes. -- Address keys – this is an auxiliary key used for the address derivation; it’s internally utilized by the protocol and does not require any action from developers. -- Incoming viewing keys – used to encrypt a note for the recipient. -- Signing keys – an optional key pair used for account authorization. - -The first three pairs are embedded into the protocol while the signing key is abstracted up to the account contract developer. - -### Nullifier keys - -Nullifier keys are presented as a pair of the master nullifier public key (`Npk_m`) and the master nullifier secret key (`nsk_m`). - -To spend a note, the user computes a nullifier corresponding to this note. A nullifier is a hash of the note hash and app siloed nullifier secret key, the latter is derived using the nullifier master secret key. To compute the nullifier, the protocol checks that the app siloed key is derived from the master key for this contract and that master nullifier public key is linked to the note owner's address. - -### Address keys - -Address keys are used for account [address derivation](../accounts/index.md). - -Address keys are a pair of keys `AddressPublicKey` and `address_sk` where `address_sk` is a scalar defined as `address_sk = pre_address + ivsk` and `AddressPublicKey` is an elliptic curve point defined as `AddressPublicKey = address_sk * G`. `pre_address` can be thought of as a hash of all account’s key pairs and functions in the account contract: `pre_address := poseidon2(public_keys_hash, partial_address)` where `partial_address := poseidon2(contract_class_id, salted_initialization_hash)` and `public_keys_hash := poseidon2(Npk_m, Ivpk_m, Ovpk_m, Tpk_m)`. - -:::note -Under the current design Aztec protocol does not use `Ovpk` (outgoing viewing key) and `Tpk` (tagging key). However, formally they still exist and can be used by developers for some non-trivial design choices if needed. -::: - -### Incoming viewing keys - -The incoming viewing public key (`Ivpk`) is used by the sender to encrypt a note for the recipient. The corresponding incoming viewing secret key (`ivsk`) is used by the recipient to decrypt the note. - -When it comes to notes encryption and decryption: -- For each note, there is a randomly generated ephemeral key pair (`esk`, `Epk`) where `Epk = esk * G`. -- The `AddressPublicKey` (derived from the `ivsk`) together with `esk` are encrypted as a secret `S`, `S = esk * AddressPublicKey`. -- `symmetric_encryption_key = hash(S)` -- `Ciphertext = aes_encrypt(note, symmetric_encryption_key)` -- The recipient gets a pair (`Epk`, `Ciphertext`) -- The recipient uses the `address_sk` to decrypt the secret: `S = Epk * address_sk`. -- The recipient uses the decrypted secret to decrypt the ciphertext. - -### Signing keys - -Thanks to the native [account abstraction](../accounts#background/index.md), authorization logic can be implemented in an alternative way that is up to the developer (e.g. using Google authorization credentials, vanilla password logic or Face ID mechanism). In all these cases signing keys may not be relevant. ->>>>>>> dd7f5999c545228d0669536b4934667204ab6d57 +Thanks to the native [account abstraction](../accounts/index.md), authorization logic can be implemented in an alternative way that is up to the developer (e.g. using Google authorization credentials, vanilla password logic or Face ID mechanism). In these cases, signing keys may not be relevant. However if one wants to implement authorization logic containing signatures (e.g. ECDSA or Shnorr) they will need signing keys. Usually, an account contract will validate a signature of the incoming payload against a known signing public key. This is a snippet of our Schnorr Account contract implementation, which uses Schnorr signatures for authentication: -<<<<<<< HEAD #include_code is_valid_impl noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust -======= -#include_code is_valid_impl /noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust ->>>>>>> dd7f5999c545228d0669536b4934667204ab6d57 ### Storing signing keys @@ -115,7 +68,6 @@ Since signatures are fully abstracted, how the public key is stored in the contr #### Using a private note​ -<<<<<<< HEAD Storing the signing public key in a private note makes it accessible from the `entrypoint` function, which is required to be a private function, and allows for rotating the key when needed. However, keep in mind that reading a private note requires nullifying it to ensure it is up-to-date, so each transaction you send will destroy and recreate the public key so the protocol circuits can be sure that the notes are not stale. This incurs cost for every transaction. #### Using an immutable private note​ @@ -136,29 +88,6 @@ When it comes to storing the signing key in a private note, there are several de By [Shared Mutable](../../../developers/reference/smart_contract_reference/storage/shared_state.md#sharedmutable) we mean privately readable publicly mutable state. To make public state accessible privately, there is a delay window in public state updates. One needs this window to be able to generate proofs client-side. This approach would not generate additional nullifiers and commitments for each transaction while allowing the user to rotate their key. However, this causes every transaction to now have a time-to-live determined by the frequency of the mutable shared state, as well as imposing restrictions on how fast keys can be rotated due to minimum delays. -======= -Storing the signing public key in a private note makes it accessible from the entrypoint function, which is required to be a private function, and allows for rotating the key when needed. However, keep in mind that reading a private note requires nullifying it to ensure it is up-to-date, so each transaction you send will destroy and recreate the public key so the protocol circuits can be sure that the notes are not stale. - -#### Using an immutable private note​ - -Similar to using a private note, but using an immutable private note removes the need to nullify the note on every read. This generates no nullifiers and commitments per transaction. However, it does not allow the user to rotate their key if they lose it. - -#include_code public_key aztec-packages/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust - -:::note -When it comes to storing the signing key in a private note, there are several details that rely on the wallets: -- A note with a key is managed similar to any other private note. Wallets are expected to backup all the notes so that they can be restored on another device (e.g. if the user wants to move to another device). -- The note with the key might exist locally only (in PXE) or it can be broadcasted as an encrypted note by the wallet to itself. In the second case, this note will also exist on Aztec. -::: - -#### Using Shared Mutable state - -:::note -By [Shared Mutable](../shared_state#sharedmutable/index.md) we mean privately readable publicly mutable state. -::: - -To make public state accessible privately, there should be a delay window in public state updates. One needs this window to be able to generate proofs client-side. This approach would not generate additional nullifiers and commitments for each transaction while allowing the user to rotate their key. However, this causes every transaction to now have a time-to-live determined by the frequency of the mutable shared state, as well as imposing restrictions on how fast keys can be rotated due to minimum delays. ->>>>>>> dd7f5999c545228d0669536b4934667204ab6d57 #### Reusing some of the in-protocol keys @@ -172,7 +101,6 @@ Since there are no restrictions on the actions that an account contract may exec All key pairs (except for the signing keys) are generated in the [Private Execution Environment](../pxe/index.md) (PXE) when a user creates an account. PXE is also responsible for the further key management (oracle access to keys, app siloed keys derivation, etc.) -<<<<<<< HEAD ### Keys derivation All key pairs are derived using elliptic curve public-key cryptography on the [Grumpkin curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md), where the secret key is represented as a scalar and the public key is represented as an elliptic curve point multiplied by that scalar. @@ -196,40 +124,11 @@ App-siloed keys allow to minimize damage of potential key leaks as a leak of the App-siloed keys are derived from the corresponding master keys and the contract address. For example, for the app-siloed nullifier secret key: `nsk_app = hash(nsk_m, app_contract_address)`. App-siloed keys [are derived](../advanced/storage/storage_slots.md#implementation) in PXE every time the user interacts with the application. -======= -### Keys derivation - -All key pairs are derived using elliptic curve public-key cryptography on the [Grumpkin curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md). Where the secret key is represented as a scalar and the public key is represented as an elliptic curve point multiplied by that scalar. - -The address private key is an exception and derived in a way described above in the section “Address keys”. - -### The special case of escrow contracts - -Typically, for account contracts the public keys will be non-zero and for non-account contracts zero. - -An exception (a non-account contract which would have some of the keys non-zero) is an escrow contract. Escrow contract is a type of contract which on its own is an "owner" of a note meaning that it has a` Npk_m` registered and the notes contain this `Npk_m`. - -Participants in this escrow contract would then somehow get a hold of the escrow's `nsk_m` and nullify the notes based on the logic of the escrow. An example of an escrow contract is a betting contract. In this scenario, both parties involved in the bet would be aware of the escrow's `nsk_m`. The escrow would then release the reward only to the party that provides a "proof of winning". - -### App siloed keys - -All keys on Aztec (except for the signing keys) are app-siloed meaning they are scoped to the contract that requests them. This means that the keys used for the same user in two different application contracts will be different. - -App-siloed keys allow to minimize damage of potential key leaks as a leak of the scoped keys would only affect one application. - -App-siloed keys are derived from the corresponding master keys and the contract address. For example, for the app-siloed nullifier secret key: `nsk_app = hash(nsk_m, app_contract_address)`. - -App-siloed keys [are derived](../storage_slots#implementation/index.md) in PXE every time the user interacts with the application. ->>>>>>> dd7f5999c545228d0669536b4934667204ab6d57 App-siloed incoming viewing key also allows per-application auditability. A user may choose to disclose this key for a given application to an auditor or regulator (or for 3rd party interfaces, e.g. giving access to a block explorer to display my activity), as a means to reveal all their activity within that context, while retaining privacy across all other applications in the network. ### Key rotation -<<<<<<< HEAD Key rotation is the process of creating new signing keys to replace existing keys. By rotating encryption keys on a regular schedule or after specific events, you can reduce the potential consequences of the key being compromised. -======= -Key rotation is the process of creating new signing keys to replace existing keys. By rotating encryption keys on a regular schedule or after specific events, you can reduce the potential consequences of the key being compromised. ->>>>>>> dd7f5999c545228d0669536b4934667204ab6d57 On Aztec, key rotation is impossible for nullifier keys, incoming viewing keys and address keys as all of them are embedded into the address and address is unchangeable. In the meanwhile, signing keys can be rotated. diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 4f88c4e8846..c56871c9bd6 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -7,7 +7,7 @@ use dep::aztec::macros::aztec; #[aztec] pub contract SchnorrAccount { use dep::authwit::{ -:diffg LO account::AccountActions, + account::AccountActions, auth::{compute_authwit_message_hash, compute_authwit_nullifier}, auth_witness::get_auth_witness, entrypoint::{app::AppPayload, fee::FeePayload}, @@ -25,15 +25,9 @@ pub contract SchnorrAccount { #[storage] struct Storage { -<<<<<<< HEAD // docs:start:public_key signing_public_key: PrivateImmutable, // docs:end:public_key -======= - // docs:start:public_key - signing_public_key: PrivateImmutable, - // docs:end:public_key ->>>>>>> dd7f5999c545228d0669536b4934667204ab6d57 } // Constructs the contract @@ -131,4 +125,4 @@ pub contract SchnorrAccount { !is_spent & valid_in_private } -} +} \ No newline at end of file From a7a61730db25a23a179868bc88b05389f18cb729 Mon Sep 17 00:00:00 2001 From: Lisa <106527861+sthwnd@users.noreply.github.com> Date: Wed, 19 Feb 2025 14:04:51 +0200 Subject: [PATCH 18/18] incorporation of the first part of Palla's comments. --- docs/docs/aztec/concepts/accounts/index.md | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/docs/aztec/concepts/accounts/index.md b/docs/docs/aztec/concepts/accounts/index.md index 9694200ad04..8fbaa113408 100644 --- a/docs/docs/aztec/concepts/accounts/index.md +++ b/docs/docs/aztec/concepts/accounts/index.md @@ -6,11 +6,11 @@ tags: [accounts] Aztec has native account abstraction. Every account in Aztec is a smart contract. -In this section, you’ll learn about Aztec’s account abstraction, Aztec accounts and address derivation, how wallets relate to accounts, and how the entrypoints are defined. +In this section, you'll learn about Aztec's account abstraction, Aztec accounts and address derivation, how wallets relate to accounts, and how the entrypoints are defined. ## Account Abstraction (AA) -Under the Account Abstraction, the identity of a user is usually represented by a smart contract. +Under the Account Abstraction, the identity of a user is usually represented by a smart contract. That makes user's on-chain identity more flexible. For example, Bitcoin has rigid accounts that must be a private key, whereas a user might want their on-chain identity to be represented by a physical passport. Among the account parts to be abstracted are authentication (“Who I am”), authorization (“What I am allowed to do”), replay protection, fee payment, and execution. @@ -28,7 +28,7 @@ In the case of Aztec, we have native Account Abstraction. While we talk about “arbitrary verification logic” describing the intuition behind AA, the logic is usually not really arbitrary. The verification logic (i.e. what can be checked as an authorization) is limited to make the verification time fast and bounded. If it is not bounded, an attacker can flood the mempool with expensive invalid transactions clogging the network. That is the case for all chains where transaction validity is checked by the sequencer. -On Aztec, there is no limitation on verification logic, as transaction validity check is executed client-side and a proof of validity is supplied to the sequencer. The sequencer only verifies the proof and this process is independent of the verification logic complexity. +On Aztec, there is no limitation on verification logic. The sequencer only verifies the proof and this process is independent of the verification logic complexity. This unlocks a whole universe of new use cases and optimization of existing ones. Whenever the dapp can benefit from moving expensive computations off-chain, Aztec AA will provide a unique chance for an optimization. That is to say, on traditional chains users pay for each executed opcode, hence more complex operations (e.g. alternative signature verification) are quite expensive. In the case of Aztec, it can be moved off-chain so that it becomes almost free. The user pays for the operations in terms of client-side prover time. @@ -63,8 +63,6 @@ def entryPoint(payload): enqueueCall(to, data, value, gasLimit); ``` -Read more about how to write an account contract [here](../../../tutorials/codealong/contract_tutorials/write_accounts_contract.md). - A request for executing an action requires: - The `origin` contract to execute as the first step. - The initial function to call (usually `entrypoint`). @@ -90,21 +88,19 @@ Entrypoints for the following cases: ### 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 (e.g. "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. +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 (e.g. "call swap in X contract"), and the wallet encodes the request as a valid payload for the user's account contract. The account contract then validates the request encoded by the wallet, and executes the function calls requested by the dapp. ### Account initialization When a user wants to interact with the network, as their first action, they need to deploy their account contract (analogous to setting up a wallet with a seed phrase in an EOA). -However, this is not required when sitting on the receiving end. The user address can be deterministically derived from their encryption public key and the account contract they intend to deploy. So that the funds can be sent to an account that hasn’t been deployed yet. +However, this is not required when sitting on the receiving end. The user address can be deterministically derived from their encryption public key and the account contract they intend to deploy. So that the funds can be sent to an account that hasn't been deployed yet. Users will need to pay transaction fees in order to deploy their account contract. This can be done by sending fee juice to their account contract address (which can be derived deterministically, as mentioned above), so that the account has funds to pay for its own deployment. Alternatively, the fee can be paid for by another account, using [fee abstraction](#fee-abstraction). ## What is account address -Address is derived from the [address keys](keys.md#address-keys). While the AddressPublicKey is an elliptic curve point of the form (x,y), the address is its x coordinate. - -The address y coordinate can be derived when needed using the formula y = sqrt(x^3 - 17), coming from the [Grumpkin elliptic curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md). For x to be legit as an address there should exist a corresponding y that satisfies the aforementioned equation: any field element cannot work as an address. +Address is derived from the [address keys](keys.md#address-keys). While the AddressPublicKey is an elliptic curve point of the form (x,y) on the [Grumpkin elliptic curve](https://github.com/AztecProtocol/aztec-connect/blob/9374aae687ec5ea01adeb651e7b9ab0d69a1b33b/markdown/specs/aztec-connect/src/primitives.md), the address is its x coordinate. The corresponding y coordinate can be derived if needed. For x to be legit as an address there should exist a corresponding y that satisfies the curve equation: any field element cannot work as an address. ### Complete address @@ -127,7 +123,7 @@ When executing a private function, this authorization is checked by requesting a The user's [Private eXecution Environment (PXE)](../concepts/pxe/index.md) is responsible for storing these auth witnesses and returning them to the requesting account contract. Auth witnesses can belong to the current user executing the local transaction, or to another user who shared it out-of-band. -However, during a public function execution, it is not possible to retrieve a value from the local [oracle](../../smart_contracts/oracles/index.md). To support authorizations in public functions, account contracts should save in contract storage what actions have been pre-authorized by their owner. +However, during a public function execution, it is not possible to retrieve a value from the local [oracle](../../smart_contracts/oracles/index.md). To support authorizations in public functions, account contracts should save in a public authwit registry what actions have been pre-authorized by their owner. These two patterns combined allow an account contract to answer whether an action `is_valid_impl` for a given user both in private and public contexts. @@ -149,17 +145,17 @@ Nonce is a sequentially increasing number (transaction-over-transaction) and it In particular, nonce management defines what it means for a transaction to be canceled, the rules of transaction ordering, and replay protection. In Ethereum, nonce is enshrined into the protocol. For example, to cancel a transaction, its sender should send another transaction with the same nonce but with a higher transaction fee. -On the Aztec network, nonce is abstracted i.e. if a developer wants to customize it, they get to decide how they handle replay protection, transaction cancellation, as well as ordering. It’s mostly relevant to those building wallets. For example, a developer can design a wallet that allows sending big transactions with very low priority fees because the transactions are not time sensitive (i.e. the preference is that a transaction is cheap and doesn’t matter if it is slow). If one tries to apply this logic today on Ethereum (under sequential nonces), when they send a large, slow transaction they can’t send any other transactions until that first large, slow transaction is processed. +On the Aztec network, nonce is abstracted i.e. if a developer wants to customize it, they get to decide how they handle replay protection, transaction cancellation, as well as ordering. It's mostly relevant to those building wallets. For example, a developer can design a wallet that allows sending big transactions with very low priority fees because the transactions are not time sensitive (i.e. the preference is that a transaction is cheap and doesn't matter if it is slow). If one tries to apply this logic today on Ethereum (under sequential nonces), when they send a large, slow transaction they can't send any other transactions until that first large, slow transaction is processed. ### Fee abstraction -It doesn’t have to be the transaction sender who pays the transaction fees. Wallets or dapp developers can choose any payment logic they want using a paymaster. +It doesn't have to be the transaction sender who pays the transaction fees. Wallets or dapp developers can choose any payment logic they want using a paymaster. -Paymaster is a contract that can pay for transactions on behalf of users. +Paymaster is a contract that can pay for transactions on behalf of users. It is invoked during the private execution stage and set as the fee payer. - It can be managed by a dapp itself (e.g. a DEX can have its own paymaster) or operate as a third party service available for everyone. - Fees can be paid publicly or privately. - Fees can be paid in any token. Fee abstraction unlocks use cases like: -- Sponsored transactions (e.g. the dapp’s business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. +- Sponsored transactions (e.g. the dapp's business model might assume revenue from other streams besides transaction fees or the dapp might utilize sponsored transaction mechanics for marketing purposes). For example, sponsoring the first ten transactions for every user. - Flexibility in the currency used in transaction payments (e.g. users can pay for transactions in ERC-20 token).