Transactions
On this page you'll learn:
- The step-by-step process of sending a transaction on Aztec
- The role of components like PXE, Aztec Node, and the sequencer
- The private and public kernel circuits and how they execute function calls
- The call stacks for private and public functions and how they determine a transaction's completion
Simple Example of the (Private) Transaction Lifecycle
The transaction lifecycle for an Aztec transaction is fundamentally different from the lifecycle of an Ethereum transaction.
The introduction of the Private eXecution Environment (PXE) provides a safe environment for the execution of sensitive operations, ensuring that decrypted data are not accessible to unauthorized applications. However, the PXE exists client-side on user devices, which creates a different model for imagining what the lifecycle of a typical transaction might look like. The existence of a sequencing network also introduces some key differences between the Aztec transaction model and the transaction model used for other networks.
The accompanying diagram illustrates the flow of interactions between a user, their wallet, the PXE, the node operators (sequencers / provers), and the L1 chain.
- The user initiates a transaction – In this example, the user decides to privately send 10 DAI to gudcause.eth. After inputting the amount and the receiving address, the user clicks the confirmation button on their wallet.
- The PXE executes transfer locally – The PXE, running locally on the user's device, executes the transfer method on the DAI token contract on Aztec and computes the state difference based on the user's intention. At this point, the transaction exists solely within the context of the PXE.
- The PXE proves correct execution – The PXE proves correct execution (via zero-knowledge proofs) of the authorization and of the private transfer method. Once the proofs have been generated, the PXE sends the proofs and required inputs (new note commitments and nullifiers) to the sequencer.
- The sequencer processes the transaction – The randomly-selected sequencer (based on the Fernet sequencer selection protocol) validates the transaction proofs along with required inputs for this private transfer. The sequencer also executes public functions and requests proofs of public execution from a prover network. When the sequencer receives proofs from the prover network, the proofs are bundled into a final rollup proof.
- The transaction settles to L1 – The verifier contract on Ethereum validates the rollup proof and records a new state root. Once the state root is verified in an Ethereum transaction, the private transfer has settled and the transaction is considered final.
Detailed Diagram
The following diagram provides a more detailed overview of the transaction execution process, highlighting three different types of transaction execution: contract deployments, private transactions, and public transactions.
See the page on contract communication for more context on transaction execution.
Transaction Requests
Transaction requests are how transactions are constructed and sent to the network.
In Aztec.js:
constructor(
/** Sender. */
public origin: AztecAddress,
/** Pedersen hash of function arguments. */
public argsHash: Fr,
/** Transaction context. */
public txContext: TxContext,
/** Function data representing the function to call. */
public functionData: FunctionData,
/** A salt to make the hash difficult to predict. The hash is used as the first nullifier if there is no nullifier emitted throughout the tx. */
public salt: Fr,
) {}
Source code: yarn-project/stdlib/src/tx/tx_request.ts#L15-L28
Where:
originis the account contract where the transaction is initiated from.argsHashis the hash of the arguments of the entrypoint call. The complete set of arguments is passed to the PXE as part of the TxExecutionRequest and checked against this hash.txContextcontains the chain id, version, and gas settings.functionDatacontains the function selector and indicates whether the function is private or public.saltis used to make the transaction request hash difficult to predict. The hash is used as the first nullifier if no nullifier is emitted throughout the transaction.
An account contract validates that the transaction request has been authorized via its specified authorization mechanism, via the is_valid_impl function (e.g. an ECDSA signature).
Transaction requests are simulated in the PXE in order to generate the necessary inputs for generating proofs. Once transactions are proven, a transaction object is created and can be sent to the network to be included in a block.
Contract Interaction Methods
Most transaction requests are created as interactions with specific contracts. The exception is transactions that deploy contracts. Here are the main methods for interacting with contracts related to transactions.
simulate
/**
* Simulate a transaction and get information from its execution.
* Differs from prove in a few important ways:
* 1. It returns the values of the function execution, plus additional metadata if requested
* 2. It supports `utility`, `private` and `public` functions
*
* @param options - An optional object containing additional configuration for the simulation.
* @returns Depending on the simulation options, this method directly returns the result value of the executed
* function or a rich object containing extra metadata, such as estimated gas costs (if requested via options),
* execution statistics and emitted offchain effects
*/
public async simulate<T extends SimulateInteractionOptions>(
options: T,
): Promise<SimulationReturn<Exclude<T['fee'], undefined>['estimateGas']>>;
// eslint-disable-next-line jsdoc/require-jsdoc
public async simulate<T extends SimulateInteractionOptions>(
options: T,
): Promise<SimulationReturn<T['includeMetadata']>>;
// eslint-disable-next-line jsdoc/require-jsdoc
public async simulate(
options: SimulateInteractionOptions,
): Promise<SimulationReturn<typeof options.includeMetadata>> {
Source code: yarn-project/aztec.js/src/contract/contract_function_interaction.ts#L81-L104
send
/**
* Sends a transaction to the contract function with the specified options.
* This function throws an error if called on a utility function.
* It creates and signs the transaction if necessary, and returns a SentTx instance,
* which can be used to track the transaction status, receipt, and events.
* @param options - An object containing 'from' property representing
* the AztecAddress of the sender and optional fee configuration
* @returns A SentTx instance for tracking the transaction status and information.
*/
public send(options: SendInteractionOptions): SentTx {
Source code: yarn-project/aztec.js/src/contract/base_contract_interaction.ts#L30-L41
Batch Transactions
Batched transactions are a way to send multiple transactions in a single call. They are created by the BatchCall class in Aztec.js. This allows a batch of function calls from a single wallet to be sent as a single transaction through a wallet.
Enabling Transaction Semantics
There are two kernel circuits in Aztec, the private kernel and the public kernel. Each circuit validates the correct execution of a particular function call.
A transaction is built up by generating proofs for multiple recursive iterations of kernel circuits. Each call in the call stack is modeled as a new iteration of the kernel circuit and is managed by a FIFO queue containing pending function calls. There are two call stacks, one for private calls and one for public calls.
One iteration of a kernel circuit will pop a call off of the stack and execute the call. If the call triggers subsequent contract calls, these are pushed onto the stack.
Private kernel proofs are generated first. The transaction is ready to move to the next phase when the private call stack is empty.
The public kernel circuit takes in proof of a public/private kernel circuit with an empty private call stack, and operates recursively until the public call stack is also empty.
A transaction is considered complete when both call stacks are empty.
The only information leaked about the transaction is:
- The number of private state updates triggered
- The set of public calls generated
The addresses of all private calls are hidden from observers.
Next Steps
- Learn about accounts and how they authorize transactions
- Understand state management and how transaction effects are stored
- Explore the PXE in more detail