Research Report: Abstract Introduction to Aztec Accounts

Author: ChiHaoLu (chihaolu.eth) Source: medium Translation: Shanoba, Golden Finance

This article focuses on the development of Account Abstraction (AA) in the Aztec Layer 2 solution and related contents. I cite a number of Aztec’s official resources, including official documentation, blogs, and tutorials. Please find these excellent resources in the references section at the end of the article!

Background

Due to the increased complexity of Aztec compared to other Native AA implementations in ZK-Rollups, readers may benefit from having some context to better understand this article.

  • Familiarity with smart contract wallets and their features
  • Familiarity with EIP-4337
  • Familiarity with native account abstraction in ZK-Rollups
  • UTXO basic concepts
  • The basic concept of ZK-Rollups
  • Basic concepts of zero-knowledge proof programs

Introduction

Take a quick look at Aztec

Aztec is an open-source Layer 2 network designed to provide scalability and privacy protection for Ethereum. Aztec leverages zkSNARK proofs to provide privacy protection and scalability through the ZK-Rollup service. Users of Aztec do not need any trusted third parties or additional consensus mechanisms to access private transactions.

We all know that in traditional ZK-Rollups, “ZK” doesn’t necessarily mean privacy; It means using zero-knowledge proofs (ZKPs) to prove that certain computations have been performed correctly off-chain. However, in Aztec, privacy is implemented in ZK-Rollup in addition to scalability. Digging deeper, in the past, the details of each transaction were publicly visible on-chain, but at Aztec, the input and output of each transaction are encrypted. These transactions are verified by ZKP to prove that the encrypted information is accurate and originated in plaintext. Only the user who constructs these private transactions knows the actual plaintext information.

Even important characters in ZK-Rollup, such as Sequencer and Prover, can’t determine what the plaintext is. All information about the transaction, including the sender, receiver, transaction data, and the value of the transfer, is hidden. Although only the users themselves know the details of the transaction, they can still trust the correctness of the transaction. This confidence stems from the fact that only legitimate transactions can generate valid zero-knowledge proofs to prove their accuracy.

How to implement private transactions and how to verify their fundamentals is a big topic that is beyond the scope of this article. In simple terms, what we need is an “additional layer for zero-knowledge proof verification” to validate ZKP lists, each of which validates a private transaction. That’s why they’re called “ZK-ZK-Rollups”.

What is Noir?

In Aztec, a native account abstraction is used, which means that there is no difference between an externally owned account (EOA) and a contract account. All accounts are smart contracts. Therefore, we will give a brief overview of Aztec’s contract development ecosystem, as it is crucial to understand contract development. However, if you don’t plan to develop the account contract yourself, then reading and understanding this article does not require you to turn on your computer and write the contract. You just need to understand the logic in the account contract code. You can explore the depth of the topic at your own discretion!

Noir is a language for writing SNARK programs, similar to Circcom and ZoKrates. Not only does it allow you to automatically generate Solidity Verifier contracts after the circuit is created, but it can also be used to write your own protocols or even blockchains. Since Noir doesn’t rely entirely on Aztec (it doesn’t compile to a specific proof system), you can achieve your goals by implementing a backend server and smart contract interface for the proof system.

At Aztec, Noir is used to write smart contracts where variables (states) and functions can be protected by privacy.

What is Private Status and Private Notes

According to our understanding of public blockchains, usually all states are public. In Aztec, it is important to grasp the concept of private state and how to manage it (add, modify, delete). The private state is encrypted and owned by its holder. For example, if I’m the owner of a contract, a specific variable in that contract can be encrypted and hidden private state. Only I, as the owner of this private state, can decrypt the ciphertext to get the plaintext.

Private state is stored by attaching only the database tree. This is done because changing the state value directly can leak a lot of information from the transaction diagram. However, the database does not directly store the value of the private state. Instead, it records them as private notes in encrypted form (e.g., x=0 -> x=1) addr=0x00 -> addr=0x01. So, in reality, these private variables, while they appear to be variables, are actually made up of a bunch of immutable private notes. This is the abstraction of variables. If it’s not clear, let’s move on.

When you need to delete a private state, you can add the invalid characters related to that private state to another invalid database to invalidate it. When you need to change the private state, first invalidate it and then add a new private comment to it.

We’ll cover the concept of nullifiers shortly. You can think of it as the key you need to link a private note to its invalid character. Only the owner of an invalid key can identify and use the private note associated with it.

At this point, savvy readers may have noticed that this structure is very similar to UTXOs (unspent transaction outputs), and we can iterate over UTXOs to determine the current state of the private state (although it’s important to remember that it’s the user who signs the transaction, not the UTXO; We’ll explain that later).

! [TEg3TOpeZXF82AgjnmG7in3uwiZp3ZtIpGsNQvxc.png] (https://img.jinse.cn/7128680_watermarknone.png “7128680”)

What is a Private Note? A UTXO is called a Note, and we traverse this Note Tree to get information about the private state. When we want to change the private state, the steps are as follows:

  1. The user retrieves all the private notes related to that private status from the notes database.
  2. The user (which is actually the Aztec node that the user runs) attests locally to the existence of each retrieved annotation in this DB tree.
  3. The user adds an invalid character to prevent others from reading the same leaf again.
  4. The user inserts a new leaf (a new comment) to update the value of this private state.

Aztec’s AA mechanism

What is the protocol layer and what is the application layer?

As we all know, EIP-4337 aims to move the entire transaction process to the application layer, implement an open relay system, and abstract the signature (verification mechanism) and payment model through the programmable nature of smart contracts. However, for Native Account Abstraction, whether in the StarkNet, zkSync era, or Aztec, which is the focus of this article, certain elements need to be etched into Layer 2’s protocol in order to function properly. For example, in Aztec, encryption keys and invalid keys need to be implemented at the protocol level.

Understanding native account abstraction requires a deeper understanding of how the entire chain works (assuming it’s called “native”, with AA’s execution logic naturally linked to a specific Layer2 protocol). For example, in the zkSync era, there is a need to understand the system contract, in StarkNet, there is a need to understand how the sequencer works, and in Aztec, it is crucial to understand the role of these “keys and their underlying private state”.

Account entry points and verification phases

In Aztec, unlike other implementations of account abstraction, there is no strictly defined function name (function signature) as an entry point to the account contract (e.g., validateUserOp in EIP-4337, validateTransactionzkSync Era, and __validate__StarkNet). The user is free to choose any function in the account contract as the entry point and send a transaction with the relevant parameters.

The function chosen by the user (we call it entrypoint()) must be private (executed in the user’s private execution environment). It can only be called from the account contract owner’s client (the user’s wallet). When the user’s wallet entrypoint() is executed locally, it also generates a zero-knowledge proof. This attestation informs the Aztec protocol of the verification phase that the off-chain execution has occurred and has been successful.

Limitations of the validation phase

This also extends to the question of whether or not to impose restrictions on what can be done during the validation phase. It is well known that since there is no cost limit for validating transactions (essentially, validating transactions is a function view), an attacker can perform a denial-of-service (DOS) attack on the mempool to compromise the bundler (EIP-4337) or the operator/sequencer (native AA). EIP-4337 defines which opcodes are prohibited and how storage access is restricted. zkSync Era relaxes some OpCode usage, while StarkNet does not allow external contract calls at all.

Because the Aztec protocol involves the client validating an attached zero-knowledge proof, rather than actually calling a validation function to determine that the result is or , trueAztecfalse, unlike other protocols, does not impose any restrictions during the validation phase. The contract entrypoint in the account can freely call other contracts, access any storage, and perform any calculations.

Interaction Flow

In more detail, in zkSync Era and StarkNet, only the “account contract” can initiate a transaction, because the protocol calls a specified function as an entry point, imposing this restriction. However, in Aztec, “all contracts” can initiate transactions, as there is no limit to which function the protocol calls as the entry point. This means that on Aztec, it is no longer a fixed interaction flow where a user sends a transaction to a specific role (such as the EntryPoint contract in EIP-4337 or the sequencer/operator in Native AA) and then invokes the target contract. Users can send transactions through the wallet, and directly let the target contract complete the relevant interactions, which greatly enhances flexibility.

If you’re familiar with EIP-2938, another implementation of AA, you’ll find that Aztec is more akin to the multi-tenant approach in it. However, this is a deeper topic that you can explore on your own.

Keys in your Aztec account

In Aztec, each account typically has two master keys: the signing key and the privacy master key.

Signing key

The signing key is used to represent the user’s authorization to perform a specific action using the private key. A simple example is when a user records a public key derived from the signing key in the account contract. The generated signature can then be restored within the contract by signing the transaction or message using this signing key to check that it matches the public key recorded in the contract (also known as the owner-controlled key, which is stored in the form of a key). addressSolidity wallet contract).

The choice of the algorithm for the elliptic curve of digital signatures is up to the user. For example, Ethereum uses secp256k1, while Aztec provides an example schnorr to use. In the account contract, the entrypoint() function acts as an entry point (the source of the call) and is used by the validation logic (is_valid_impl()) to check if the Schnorr signature matches the public key of record std::schnorr::verify_signature(…).

! [eWwFvxmMF7pNt0axLcucSvjcig6QHMXl2HKH3luz.png] (https://img.jinse.cn/7128683_watermarknone.png “7128683”)

The signing key is essentially the same as the owner-controlled key in the smart contract wallet that we are familiar with, so it should be relatively easy to understand. In fact, signing keys are not absolutely necessary. If the account developer has implemented a different verification mechanism, the account may not have a signing key.

Privacy Master Key

The privacy master key in your Aztec account is non-transferable; Each Aztec account is tied to a private master key. The private master key derives the public master key, which is then hashed with the contract code to generate the address of the account contract.

! [9WujRrvfd8YccfQkh9kbCtz0O1GVAKvNVJI7kXtm.png] (https://img.jinse.cn/7128684_watermarknone.png “7128684”)

We public_master_key the user’s account_address, partial_address, and collectively as the full address of the account. When dealing with private state, the user needs to provide these three pieces of information so that anyone can verify that the public key corresponds to the intended address.

However, if it is an public_master_key application that does not intend to handle private state and is missing (e.g. DeFi), you can simply fill in that public_master_key field 0 to indicate that it does not wish to receive private notes.

So, while Aztec allows us to implement a verification mechanism and even some recovery mechanism in the account contract to enhance account security, the mechanism related to the privacy master key is printed in the protocol and tied to the address. Therefore, it is not interchangeable.

The implication here is that this key is just as important as the private key of an EOA (externally owned account) in Ethereum, making it a single point of failure (SPoF) for an account. If a user loses or an account’s privacy master key is stolen, there is no doubt that the account will not be recoverable.

The privacy master key also uses a process similar to BIP-32 to export encryption keys and invalid keys. Users can use different encryption keys and invalid keys in different applications or actions to ensure privacy and security.

Encryption key

The public key of the encryption key is used to encrypt the private note, while the private key is used to decrypt it. For example, in a token transfer scenario, if I (the token sender) want to transfer tokens to my friend (token recipient), I need to encrypt the private note (the transfer of tokens involves changing variables, essentially balance is to change the private state variable UTXO using my friend’s cryptographic public key) and send it.

From an outsider’s point of view, without knowing the token recipient’s cryptographic private key, they can’t decipher this private note or know who the token recipient is.

Null character

Each time a private note is used, an invalid character derived from that private comment is generated (encrypted with an invalid key). This mechanism is used to prevent double-spending (to prevent others from using the same method to determine the location of a banknote or to deduct funds twice) because the Aztec protocol checks if invalid characters are unique. In order to match that invalid character to a private note, an invalid private key is required to decrypt it, so only its owner can establish a relationship between the two.

Aztec Transactions

Describing the concept of a transaction in Aztec can be challenging because it can be easily confused with a UTXO (Private Notes) and the execution mode of a transaction in EVM and Aztec is completely different.

In Aztec, every transaction is broadcast in the form of a zero-knowledge proof (obviously for privacy reasons). Users must perform computations locally on their nodes (wallet applications or clients) to generate proofs corresponding to transactions, rather than simply sending transaction objects to the miner’s mempool or any Layer 2 operator via RPC API as we used to do.

Transaction hashes and nonces

When a user creates a transaction locally, there are two important elements:

  1. Sender Address: This represents the address of the account contract that processes the transaction. In this account contract, there is the entry point we mentioned earlier, which serves as a verification function for external parties to verify whether the action (transaction) is authorized by the account owner.
  2. Payload data: This includes information about the transaction, such as signature, destination contract address, value, data, etc.

! [xReWU4p6VrxP45q5EYNXZGAziaZhIG1DTrjeO4yD.png] (https://img.jinse.cn/7128688_watermarknone.png “7128688”)

At Aztec’s protocol level, the hash of each valid transaction is used as a means to prevent the same transaction from being executed multiple times. The account contract developer can decide whether there should be a nonce in the account contract and the associated logic. For example, they can set requirements such as ensuring that the nonce field in the transaction is strictly increased or that the transaction can be processed in any order.

Since there is no strict nonce requirement at the protocol level, Aztec is unable to cancel a pending transaction by submitting a new transaction with the same nonce and higher gas fees.

Execute the request

As mentioned earlier, the user can specify any function in the account contract as an entry point depending on the situation, and the operation in Aztec is not controlled by a simple trading object. In fact, what tells the contract account what to do is an object called an “execution request”. The object represents the user’s behavior, such as “Call the transfer function on the contract with 0x1234 of the following parameters”.

The user initiates a transaction locally in the wallet, where the sender_address is the address of the account contract, containing the relevant encoding data of the function in the payload call target contract transfer(), and the signature that can be verified by the account contract. The wallet converts these two elements into an execution request.

The wallet then enters a private note, encryption key, or invalid secret into a local virtual machine to simulate this execution request. During the simulation process, an execution trace will be generated, which will be handed over to the prover to generate a zero-knowledge proof. This proof shows that these calculations (the execution of private functions) are indeed done locally by the user.

Through this process, we get two pieces of information: proof and private_data (the output of the private kernel circuit for this transaction). The wallet then sends the transaction object containing these two pieces of information to Aztec’s Sequencer mempool and completes the transaction.

Client-based ZKP instead of EVM type execution environment

In Aztec, we don’t simply input all the information into a Turing machine like the EVM to generate the updated state. Instead, it relies on the circuitry within each Dapp to determine how the privacy information should work. This means that Dapp developers need to have a way to prove the state of contract variables. For example, let’s consider the user’s balance in an ERC-20 token contract. If we want to transfer 10 DAI tokens, the contract may have the following logic:

  1. Check if the sender’s balance is greater than 10 DAI (i.e., > 10 DAI).
  2. If the sender has sufficient balance, an invalid character is created to indicate the destruction of the sender’s 10 DAI (which can offset the sender’s possession of the 10 DAI private note).
  3. Create a new private note for the recipient.
  4. Broadcast and encrypt all messages transmitted with 10 DAI.

From an outsider’s point of view, they can only see new nullities and comments appear, and they’re all encrypted. So, everyone knows that a new deal has taken place, but what exactly is going on inside is known only to the participants involved.

! [B2NwqMIhntBOllrlchIWeaetEbkSSdoTARJiM27A.png] (https://img.jinse.cn/7128690_watermarknone.png “7128690”)

Learn more about Aztec account contracts

Wallet

Wallets in Aztec are an important part of managing users’ interactions with the blockchain and their private data. Here’s a summary of the tasks that the Aztec wallet has to handle:

  1. Create an account: The wallet should allow users to create new account contracts, which essentially means deploying new account contracts on the blockchain.
  2. Private key management: The wallet is responsible for managing the user’s seed phrase and private key, including the privacy master key and signing key (depending on the design of the account contract). This management can also be extended to hardware wallet integration.
  3. View accounts: Users should be able to view their accounts and related statuses, including balances and other private statuses. This means that the wallet needs to maintain a local database of all the private notes related to the user.
  4. Interact with Dapps: Wallets need to facilitate interaction between users and Dapps. When a user interacts with a Dapp, the Dapp may request a transaction to be sent from the user’s account.
  5. User Consent: Dapps may require user consent to display certain contract states, such as the balance in the token contract. To expose these states, the wallet must be able to receive user requests (as exposing balances requires user key consent).
  6. Generate proof: To send a transaction on behalf of a user, the wallet must be able to generate a proof locally. This involves creating and processing zero-knowledge proofs of the validity of transactions.

A key point to note is that the wallet needs to scan all blocks starting with the genesis block, use the user’s key to discover and decrypt the relevant private notes, and then store them in a local database for future use. This is critical to facilitate user interaction and ensure that private state data can be effectively managed.

You mentioned another important aspect of Aztec, which is the need for users to broadcast the full address. When a wallet creates a crypto note for the recipient of a target transaction, it also needs to be able to obtain the recipient’s full address. This can be achieved by manually entering or maintaining a local database of the recipient’s address. For more details on this, please refer to the official Aztec documentation.

Account Contract

In Aztec, the main tasks of an account contract are to verify signatures (confirming that transactions are authorized by the account owner, and therefore more broadly authorized, rather than necessarily “verified by a specific digital signature algorithm”), manage gas consumption, and invoke other contracts.

This is an official example of an Aztec account contract using the Schnorr signature algorithm. The entry point for all transactions is the entrypoint() function (you are free to choose the function or name as the starting point, but in this case entrypoint() is used).

! [LahY9kfNGKkYkSm5ULPlReKATiTA3K5bjFGIClh0.png] (https://img.jinse.cn/7128692_watermarknone.png “7128692”)

When we call the entrypoint() account with the attachment payload, the account contract entrypoint() will call the Aztec AA account library entrypoint().

! [OskBKScb0LqWpcTMIuhBMjeSB151sFvSWbwXl.png] (https://img.jinse.cn/7128697_watermarknone.png “7128697”)

Aztec does not require our account contracts to import into EntrypointPayload and Aztec AA account libraries; You are free to design your own account contract logic.

! [HNskT1CkOOPiZZaAVvqlJNf7L5VxU39Fz9ir2Ica.png] (https://img.jinse.cn/7128698_watermarknone.png “7128698”)

is the context, an object that is available in every function in Aztec.nr. Contains all the kernel information required for the execution of the context application. Quoted from the official Aztec documentation. - What is the background

The above is the code entrypoint() of the Aztec AA account library. It is responsible for determining whether the operation is authorized by the account owner based on the validation function () defined on the account is_valid_impl contract, and makes the necessary calls to implement the _calls operation required for the transaction.

This means that if you want to reference this Aztec AA library, and your account contract does not have is_valid_impl implementation with the same function signature, this step will fail.

! [QZuhkG4BTiKMQF4hFZKGw0gi9MBlU5VGcDjyQyU9.png] (https://img.jinse.cn/7128699_watermarknone.png “7128699”)

Another key implementation detail is to use get_auth_witness() to retrieve signatures. You can refer to the references below to learn more about how Witnesses work. In simple terms, a witness is “an authorization to the user to do what they want to do”.

An authentication witness is a scheme for verifying operations on Aztec, so users can allow third parties, such as protocols or other users, to perform actions on their behalf. Quoted from the official Aztec documentation. — Certified Witnesses

The reason why it is called “witness” rather than simply “signature” is because the way the account contract is verified does not necessarily involve verifying the signature.

For example, let’s say there’s an operation that transfers 1000 tokens from Alice’s account to a DeFi platform. In this case, the token contract needs to query Alice’s account contract to see if she approves the “action”. This “action” requires an authentication witness to be generated in Alice’s wallet (locally), which can then be verified through her account contract. If the account contract is successfully verified, the token contract will know that the “action” has been authorized.

! [WJkuu8NWicgcHvCyH08YpHhjYtTtYiP8GbRxwwKs.png] (https://img.jinse.cn/7128700_watermarknone.png “7128700”)

Conclusion

As of now, Aztec has not implemented a fee mechanism, and their goal is also to abstract the payment of fees. This means that for a transaction to be considered valid, it must prove that it has locked in enough funds to cover its own fees. However, it does not specify where these funds must come from, making payments or in-kind payments easily possible through instant exchange.

View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • Comment
  • Repost
  • Share
Comment
0/400
No comments
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate App
Community
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)