Research Report Introduction to Aztec Account Abstraction

Exploring Aztec Account Abstraction A Comprehensive Introduction

Author: ChiHaoLu (chihaolu.eth) Source: medium Translation: Shao Ou Ba, Lian Guai

This article mainly introduces the development and related content of Account Abstraction (AA) in the Aztec Layer2 solution. I have quoted a large number of official resources from Aztec, including official documents, blogs, and tutorials. Please find these excellent resources in the reference section at the end of the article!

Background

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

Introduction

A Quick Look at Aztec

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

We all know that in traditional ZK-Rollups, “ZK” does not necessarily mean privacy; it means using zero-knowledge proofs (ZKP) to prove that certain computations have been correctly executed off-chain. However, in Aztec, in addition to scalability, ZK-Rollup also achieves privacy. In-depth, in the past, the details of every transaction were publicly visible on the chain, but in Aztec, both the inputs and outputs of each transaction are encrypted. These transactions are verified through ZKP to prove the accuracy of the encrypted information and its origin. Only the users who construct these private transactions know the actual plaintext information.

Even important roles in ZK-Rollup, such as Sequencers and Provers, cannot determine the plaintext content. All information about the transaction, including the sender, receiver, transaction data, and transferred value, is hidden. Although only the users themselves know the transaction details, they can still have confidence in 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.

The basic principles of implementing private transactions and how to verify them are a big topic beyond the scope of this article. In simple terms, what we need is an “additional layer for zero-knowledge proof verification” to verify a list of ZKPs, with each verifying a private transaction. This is also why they are called “ZK-ZK-Rollups”.

What is Noir?

In Aztec, with native account abstraction, it means that there is no distinction between externally owned accounts (EOA) and contract accounts. All accounts are smart contracts. Therefore, we will briefly introduce Aztec’s contract development ecosystem because understanding contract development is crucial. However, if you do not intend to personally develop account contracts, reading and understanding this article does not require you to open your computer and write contracts. You only need to understand the logic in account contract code. You can decide on your own how deep you want to explore this topic!

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

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

What are private states and private notes?

Based on our understanding of public blockchains, typically all states are public. In the Aztec language, understanding the concept of private states and how to manage them (add, modify, delete) is crucial. Private states are encrypted and owned by their holders. For example, if I am the owner of a contract, specific variables in that contract can be encrypted and hidden as private states. Only I, as the owner of this private state, can decrypt the ciphertext to obtain the plaintext.

Private states are stored by attaching them to a database tree. This is done because directly modifying the state values could leak a lot of information from the transaction graph. However, the database does not directly store the values of private states. 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, though appearing as variables, are actually composed of a set of immutable private notes. This is an abstraction of variables. If it is still not clear, let’s move on.

When you need to delete a private state, you can add the corresponding invalid symbol associated with that private state to another database of invalid symbols to render it invalid. When you need to modify a private state, you first invalidate it and then add a new private note to it.

We will soon introduce the notion of invalidator, which you can think of as the key required to link private notes to their invalid symbols. Only the owner of the invalidation key can recognize and use the private notes associated with it.

By now, astute readers might have noticed that this structure is very similar to UTXO (Unspent Transaction Output) where we traverse UTXOs to determine the current state of private states (although it’s important to note that it is the user signing the transaction, not the UTXOs; we will explain this later).

TEg3TOpeZXF82AgjnmG7in3uwiZp3ZtIpGsNQvxc.png

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

  1. The user retrieves all private notes related to that private state from the note database.

  2. The user (actually, the Aztec node running on the user’s machine) locally proves the existence of each retrieved note in this DB tree.

  3. The user adds an invalid symbol to prevent others from reading the same leaf again.

  4. The user inserts a new leaf (a new note) to update the value of this private state.

Aztec’s AA Mechanism

What is the Protocol Layer and 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 features of smart contracts. However, for Native Account Abstraction, whether it’s in the StarkNet, zkSync era, or the focus of this article, Aztec, certain elements need to be carved into the Layer2 protocol to operate properly. For example, in Aztec, encryption keys and invalid keys need to be implemented at the protocol level.

Understanding this native account abstraction requires a deeper understanding of how the entire chain operates (assuming it is referred to as “native,” the execution logic of AA naturally links to the specific Layer2 protocol). For example, in the zkSync era, understanding system contracts is required. In StarkNet, understanding how the sequencer works is necessary. And in Aztec, it is crucial to understand the role of these “keys and their underlying private states.”

Account Entry Points and Verification Phase

In Aztec, unlike other implementations of account abstraction, there are no strictly defined function names (function signatures) as entry points for account contracts (e.g., validateUserOp in EIP-4337, validateTransaction in the zkSync Era, and __validate__ in StarkNet). Users are free to choose any function in the account contract as the entry point and send transactions with the relevant parameters.

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

Limitations in the Verification Phase

This also extends to the question of whether restrictions are imposed on executable operations during the verification phase. As we all know, since verifying transactions has no cost limitations (essentially, verifying transactions is calling the view function), attackers can launch denial-of-service (DOS) attacks on the memory pool to disrupt bundlers (EIP-4337) or operators/sequencers (Native AA). EIP-4337 defines which opcodes are prohibited and how storage access is limited. The zkSync Era relaxes the use of some opcodes, while StarkNet does not allow external contract calls at all.

As the Aztec protocol involves the client’s verification of additional zero-knowledge proofs rather than actually calling a verification function to determine the result as true or false, unlike other protocols, no restrictions are imposed during the verification phase. The contract entrypoint within an account can freely call other contracts, access any storage, and perform any computation.

Interaction Flow

To elaborate further, in the zkSync Era and StarkNet, only the “account contract” can initiate transactions because the protocol calls a specified function as the entry point, imposing this limitation. However, in Aztec, “all contracts” can initiate transactions because the protocol does not impose any limitations on which function to call as the entry point. This means that on Aztec, it is no longer the users who send transactions to specific roles (e.g., EntryPoint contract in EIP-4337 or sequencer/operator in Native AA), and instead, users can directly send transactions through their wallets to the target contract, greatly enhancing flexibility.

If you are familiar with EIP-2938 (another implementation of AA), you will find that Aztec is more similar to its multi-tenant approach. However, this is a deeper topic that you can explore on your own.

Keys in Aztec Accounts

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

Signing Key

The signing key is used to represent the user’s authorization to perform specific operations with the private key. A simple example is when the user records the public key derived from the signing key in the account contract. Then, by using this signing key to sign transactions or messages, the generated signature can be recovered within the contract to check if it matches the recorded public key (also known as the owner control key, stored in key form).

The choice of the digital signature elliptic curve algorithm is up to the user. For example, Ethereum uses secp256k1, while Aztec provides an example using schnorr. In the account contract, the entrypoint() function serves as the entry point (called from), and the validation logic (is_valid_impl()) uses to check if the Schnorr signature matches the recorded public key std::schnorr::verify_signature(…).

eWwFvxmMF7pNt0axLcucSvjcig6QHMXl2HKH3luz.png

The signing key is essentially the same as the owner control key in a familiar smart contract wallet, so it should be relatively easy to understand. In fact, the signing key is not absolutely necessary. If the account developer implements other verification mechanisms, the account may not have a signing key.

Privacy Master Key

The privacy master key in an Aztec account is non-transferable; each Aztec account is bound to one privacy master key. The privacy master key derives a public master key, which is then hashed together with the contract code to generate the address of the account contract.

9WujRrvfd8YccfQkh9kbCtz0O1GVAKvNVJI7kXtm.png

We refer to the user’s account_address, LianGuairtial_address, and public_master_key collectively as the complete address of the account. When dealing with private states, users need to provide these three pieces of information so that anyone can verify if the public key corresponds to the expected address.

However, if it is an application (such as DeFi) that does not intend to handle private states and lacks the public_master_key, you can simply fill in the public_master_key field with 0 to indicate that it does not expect to receive private notes.

So, although Aztec allows us to implement verification mechanisms and even some recovery mechanisms in the account contract to enhance account security, the mechanisms associated with the privacy master key are printed in the protocol and bound to the address. Therefore, it is not interchangeable.

The implication here is that the key is equally important as the private key of an External Owned Account (EOA) in Ethereum, making it a Single Point of Failure (SPoF) for the account. If the user loses or the private key of the account’s privacy master key is stolen, there is no doubt that the account will be irrecoverable.

The privacy master key also uses a process similar to BIP-32 to derive the cryptographic keys and invalid keys. Users can use different cryptographic keys and invalid keys in different applications or operations to ensure privacy and security.

Cryptographic Keys

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

From an outsider’s perspective, without knowing the token receiver’s cryptographic private key, they cannot decipher this private note or identify who the token receiver is.

Invalid Symbols

Each time a private note is used, an invalid symbol (encrypted using an invalid key) derived from that private note is generated. This mechanism is used to prevent double spending (preventing others from using the same method to determine the location of the note or deduct funds twice) because the Aztec protocol checks if the invalid symbol is unique. To match the invalid symbol with the private note, the invalid private key is required for decryption, so only its owner can establish the relationship between the two.

Aztec Transactions

Describing the concept of transactions in Aztec can be challenging, as it is easy to confuse them with UTXOs (private notes), and the execution patterns of transactions in EVM and Aztec are completely different.

In Aztec, each transaction is broadcast in the form of zero-knowledge proofs (evidently for privacy reasons). Users must perform the computation locally on their nodes (wallet applications or clients) to generate the proofs corresponding to the transactions, instead of simply sending the transaction object to the miner’s memory pool or any second-layer operator as we used to do through RPC APIs.

Transaction Hash and Random Numbers

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

  1. Sender Address: This represents the address of the account contract that handles the transaction. Inside this account contract, there is the entry point we mentioned earlier, which serves as the external parties’ verification of whether this behavior (transaction) is authorized by the account owner.

  2. Payload Data: This includes information about the transaction, such as signatures, target contract address, value, data, etc.

xReWU4p6VrxP45q5EYNXZGAziaZhIG1DTrjeO4yD.png

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

Due to the lack of strict random number requirements at the protocol level, Aztec cannot cancel pending transactions by submitting new transactions with the same random number and higher gas fees.

Execution Requests

As mentioned earlier, users can specify any function in the account contract as an entry point based on different situations, and the operations in Aztec are not controlled by a simple transaction object. In fact, what tells the contract account what to do is an object called the “execution request”. This object represents the user’s actions, such as “calling the transfer function on the contract with the parameters below: 0x1234“.

Users initiate transactions locally in their wallets, where sender_address is the address of the account contract, containing the encoded data for calling the target contract’s function, transfer(), and the signature that the account contract can verify. The wallet converts these two elements into an execution request.

Then, the wallet inputs private notes, encryption keys, or invalid secrets into a local virtual machine to simulate this execution request. The simulation process generates an execution trace, which is handed over to the prover to generate a zero-knowledge proof. This proof demonstrates that these computations (execution of private functions) were indeed performed by the user locally.

Through this process, we obtain 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 the Aztec Sequencer mempool and completes the transaction.

Client-side ZKP-based Type Execution Environment Instead of EVM

In Aztec, we do not simply input all the information into a Turing machine like the EVM to generate updated states. Instead, it relies on the circuit within each Dapp to determine how privacy information should work. This means Dapp developers need a way to prove the state of contract variables. For example, let’s consider the balance of a user in an ERC-20 token contract. If we want to transfer 10 DAI tokens, the contract might 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 enough balance, create a nullifier to represent the destruction of the sender’s 10 DAI (this nullifier can offset the sender’s possession of the private note for 10 DAI).

  3. Create a new private note for the recipient.

  4. Broadcast and encrypt all messages involving the transfer of 10 DAI.

From an outsider’s perspective, they can only see new nullifiers and notes appearing, and they are encrypted. So, everyone knows that a new transaction has occurred, but only the participating parties know what actually happened inside.

B2NwqMIhntBOllrlchIWeaetEbkSSdoTARJiM27A.png

Understanding Aztec Account Contracts in Depth

Wallets

The wallet in Aztec is an important component that manages the interaction between users and the blockchain, as well as their private data. Here is a summary of the tasks that Aztec wallets must handle:

  1. Create 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 user seed phrases and private keys, which include privacy master keys and signing keys (depending on the design of the account contract). This management can also extend to hardware wallet integration.

  3. View Account: Users should be able to view their accounts and relevant states, including balances and other private states. This means the wallet needs to maintain a local database that contains all private notes related to the user.

  4. Interact with Dapps: The wallet needs to facilitate interaction between users and Dapps. When users interact with a Dapp, the Dapp may request transactions to be sent from the user’s account.

  5. User Consent: Dapps may require user consent to display certain contract states, such as balances in token contracts. To expose these states, the wallet must be able to receive user requests (as displaying balances requires user key consent).

  6. Generate Proofs: To send transactions on behalf of users, the wallet must be able to generate proofs locally. This involves creating and processing zero-knowledge proofs for transaction validity.

One key point to note is that the wallet needs to scan all blocks starting from the genesis block, using the user’s key to discover and decrypt relevant private notes, then store them in a local database for future use. This is crucial for facilitating user interaction and ensuring effective management of private state data.

You mentioned another important aspect of Aztec, which is the need for users to broadcast the complete address. When the wallet creates encrypted notes for the recipient of the target transaction, it also needs to be able to obtain the recipient’s complete address. This can be achieved through manual input or maintaining a local database of recipient addresses. For more details on this aspect, refer to the official Aztec documentation.

Account Contract

In Aztec, the main tasks of an account contract are to validate signatures (confirm that the transaction is authorized by the account owner, so it is more about authorization in a broader sense rather than specifically “verifying with a particular 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 any function or name as the entry point, but in this example, entrypoint() is used).

LahY9kfNGKkYkSm5ULPlReKATiTA3K5bjFGIClh0.png

When we call the entrypoint() of the account LianGuaiyload with attachments, the entrypoint() of the Aztec AA account library will be called.

vAROskBKScb0LqWpcTMIuhBMjeSB151sFvSWbwXl.png

Aztec does not require us to import our account contract EntryPointLianGuaiyload and Aztec AA account library; you are free to design your own account contract logic.

HNskT1CkOOPiZZaAVvqlJNf7L5VxU39Fz9ir2Ica.png

Aztec.nr is an object available in every function within the context. It contains all the kernel information required by the context application execution. Taken from the Aztec official documentation. – What is the background.

Above is the code entrypoint() of 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 contract is_valid_impl(), and performs the necessary calls through execute_calls to achieve the required transaction operations.

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

QZuhkG4BTiKMQF4hFZKGw0gi9MBlU5VGcDjyQyU9.png

Another key implementation detail is to retrieve the signature using get_auth_witness(). You can refer to the introduction in the reference material below to understand how witnesses work in detail. Simply put, a witness is “authorization for the action the user wants to perform”.

Identity verification witnesses are a way to verify operations on Aztec, so that users can allow third parties (such as protocols or other users) to act on their behalf. Taken from the Aztec official documentation. – Authentication Witnesses

It is called “witness” instead of simply “signature” because the verification of the account contract does not necessarily involve verifying the signature.

For example, suppose there is an operation to transfer 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 this “action”. This “operation” requires generating an authentication witness in Alice’s wallet (locally), which can then be verified through her account contract. If the account contract validation is successful, the token contract knows that this “action” has been authorized.

WJkuu8NWicgcHvCyH08YpHhjYtTtYiP8GbRxwwKs.png

Conclusion

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

We will continue to update Blocking; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Market

BlackRock and VanEck have submitted revised S-1 forms for a bitcoin ETF to address the SEC's recent comments.

The applicants have promptly addressed the SEC's comments on their S-1 forms in order to prepare for a potential appr...

Market

The Bitcoin Party: Euphoria and Rate Cuts

Cryptocurrency analysts predict a Santa Claus rally in the market, anticipating Bitcoin to potentially reach $48K as ...

Blockchain

Foresight Ventures Acquires The Block: A Crypto Power Move!

Fashionista Acquisition Foresight Ventures Takes $60 Million Majority Stake in Crypto News Media Firm, The Block

Blockchain

Revolut Partners with MetaMask to Simplify Crypto Purchase through Revolut Ramp 💸🤝

Exciting news from Revolut! They just announced the launch of their latest product, Revolut Ramp. With this new featu...

Blockchain

Reviving FTX SEC Gives Green Light for a Compliance-Focused Comeback

Possible Approval for FTX Revival from US SEC Depends on Adherence to Regulatory Guidelines by New Leadership.

Market

The Future of Swiss Banking: St. Galler Kantonalbank and SEBA Bank Join Forces in the Crypto Universe

St. Galler Kantonalbank (SGKB), Switzerland's fifth-largest cantonal bank, has entered the digital assets market thro...