Skip to content

Commit

Permalink
Transactions section in yellow paper
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Nov 24, 2023
1 parent c0f3613 commit e6d713d
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 0 deletions.
15 changes: 15 additions & 0 deletions yellow-paper/docs/transactions/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: Transactions
---

# Transactions

A transaction is the minimal action that changes the state of the network. Transactions in Aztec have a private and a public component, where the former is executed in the user's private execution environment (PXE) and the latter by the sequencer.

A transaction is also split into three phases to [support authorization abstraction and fee payments](../gas-and-fees/gas-and-fees.md#fees): a validation and fee preparation phase, a main execution phase, and fee distribution phase.

Users initiate a transaction by sending a _transaction request_ to their local PXE, which [locally simulates and proves the transaction](./local-execution.md) and returns a [_transaction_ object](./tx-object.md) identified by a [_transaction hash_](./tx-hash.md). This transaction object is then broadcasted to the network via an Aztec Node, which checks its [validity](./validity.md), and eventually picked up by a sequencer who [executes the public component of the transaction](./public-execution.md) and includes it in a block.

import DocCardList from '@theme/DocCardList';

<DocCardList />
31 changes: 31 additions & 0 deletions yellow-paper/docs/transactions/local-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Local Execution

Transactions are initiated via a _transaction execution request_ sent from the user to their local _private execution environment_ (PXE). The PXE first executes the transaction locally in a _simulation_ step, and then generates a _zero-knowledge proof_ of correct execution. The PXE is then responsible for converting a _transaction execution request_ into a [_transaction_](./tx-object.md) ready to be broadcasted to the network.

## Execution request

A transaction execution request has the following structure. Note that, since Aztec uses full native account abstraction where every account is backed by a contract, a transaction execution request only needs to provide the contract address, function, and arguments of the initial call; nonces and signatures are arguments to the call, and thus opaque to the protocol.

| Field | Type | Description |
|----------|----------|----------|
| origin | AztecAddress | Address of the contract where the transaction is initiated. |
| functionSelector | Field | Selector (identifier) of the function to be called as entrypoint in the origin contract. |
| argsHash | Field | Hash of the arguments to be used for calling the entrypoint function. |
| txContext | TxContext | Includes contract deployment data (if this tx is used to deploy a contract), chain id, and protocol version. |
| packedArguments | PackedArguments[] | Preimages for argument hashes. When executing a function call with the hash of the arguments, the PXE will look for the preimage of that hash in this list, and expand the arguments to execute the call. |
| authWitnesses | AuthWitness[] | Authorization witnesses. When authorizing an action identified by a hash, the PXE will look for the authorization witness identified by that hash and provide that value to the account contract. |

## Simulation step

Upon receiving a transaction execution request to _simulate_, the PXE will locally execute the function identified by the given `functionSelector` in the given `origin` contract with the arguments committed to by `argsHash`. We refer to this function as the _entrypoint_. During execution, contracts may request authorization witnesses or expanded arguments from the _execution oracle_, which are answered with the `packedArguments` and `authWitnesses` from the request.

The _entrypoint_ may enqueue additional function calls, either private or public, and so forth. The simulation step will always execute all private functions in the call stack until emptied. The result of the simulation is a [_transaction_](./tx-object.md) object without an associated _proof_ which is returned to the application that requested the simulation.

In terms of circuitry, the simulation step must execute all application circuits that correspond to private function calls, and then execute the private kernel circuit until the private call stack is empty. Note that circuits are only executed, there is no witness generation or proving involved.

## Proving step

The proving step is similar to the simulation step, though witnesses are generated for all circuits and proven. Note that it is not necessary to execute the simulation step before the proving step, though it is desirable in order to provide the user with info on their transaction and catch any failed assertions early.

The output of the proving step is a [_transaction_](./tx-object.md) object with a valid _proof_ associated, ready to be broadcasted to the network.

16 changes: 16 additions & 0 deletions yellow-paper/docs/transactions/public-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Public execution

Transactions have a _public execution_ component. Once a transaction is picked up by a sequencer to be included in a block, the sequencer is responsible for executing all enqueued public function calls in the transaction. These are defined by the `data.accumulatedData.publicCallStack` field of the [transaction object](./tx-object.md), which are commitments to the preimages of the `enqueuedPublicFunctionCalls` in the transaction. The sequencer pops function calls from the stack, and pushes new ones as needed, until the public call stack is empty.

## Bytecode

Unlike private functions, which are native circuits, public functions in the Aztec Network are specified in Brillig, a zkVM-friendly bytecode. This bytecode is executed and proven in the Brillig public virtual machine. Each function call is a run of the virtual machine, and a _public kernel circuit_ aggregates these calls and produces a final proof for the transaction, which also includes the _private kernel circuit_ proof of the transaction generated during [local execution](./local-execution.md).

## State

Since public execution is run by the sequencer, it is run on the state of the chain as it is when the transaction is included in the block. Public functions operate on _public state_, an updateable key-value mapping, instead of notes.

## Reverts

Note that, unlike local private execution, public execution can _revert_ due to a failed assertion, running out of gas, trying to call a non-existing function, or other failures. If this happens, the sequencer halts execution and discards all side effects from the [transaction payload phase](../gas-and-fees/gas-and-fees.md#transaction-payload). The transaction is still included in the block and pays fees, but is flagged as reverted.

84 changes: 84 additions & 0 deletions yellow-paper/docs/transactions/tx-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Transaction object

The transaction object is the struct broadcasted to the p2p network, generated by [_local execution_](./local-execution.md) by the user's PXE. Sequencers pick up transactions from the p2p network to include in a block.

## Transaction object struct

The fields of a transaction object are the following:

| Field | Type | Description |
|----------|----------|----------|
| data | PrivateKernelPublicInputsFinal | Public inputs (ie output) of the last iteration of the private kernel circuit for this transaction. |
| proof | Buffer | Zero-knowledge honk proof for the last iteration of the private kernel circuit for this transaction. |
| encryptedLogs | Buffer[][] | Encrypted logs emitted per function in this transaction. Position `i` contains the encrypted logs emitted by the `i`-th function execution. |
| unencryptedLogs | Buffer[][] | Equivalent to the above but for unencrypted logs. |
| enqueuedPublicFunctionCalls | PublicCallRequest[] | List of public function calls to run during public execution. |
| newContracts | ExtendedContractData[] | List of new contracts to be deployed as part of this transaction. |

### Private kernel public inputs final

Output of the last iteration of the private kernel circuit. Includes _accumulated data_ after recursing through all private function calls, as well as _constant data_ composed of _historic block data_ reflecting the state of the chain when such functions were executed, and the global _transaction context_. Refer to the circuits section for more info.

**Accumulated data**

| Field | Type | Description |
|-------|------|-------------|
| aggregationObject | AggregationObject | Aggregated proof of all the previous kernel iterations. |
| newCommitments | Field[] | The new commitments made in this transaction. |
| newNullifiers | Field[] | The new nullifiers made in this transaction. |
| nullifiedCommitments | Field[] | The commitments which are nullified by a nullifier in the above list. |
| privateCallStack | Field[] | Current private call stack. |
| publicCallStack | Field[] | Current public call stack. |
| newL2ToL1Msgs | Field[] | All the new L2 to L1 messages created in this transaction. |
| encryptedLogsHash | Field[] | Accumulated encrypted logs hash from all the previous kernel iterations. |
| unencryptedLogsHash | Field[] | Accumulated unencrypted logs hash from all the previous kernel iterations. |
| encryptedLogPreimagesLength | Field | Total accumulated length of the encrypted log preimages emitted in all the previous kernel iterations. |
| unencryptedLogPreimagesLength | Field | Total accumulated length of the unencrypted log preimages emitted in all the previous kernel iterations. |
| newContracts | NewContractData[] | All the new contracts deployed in this transaction. |

**Historic block data**

| Field | Type | Description |
|-------|------|-------------|
| noteHashTreeRoot | Field | Root of the note hash tree at the time of when this information was assembled. |
| nullifierTreeRoot | Field | Root of the nullifier tree at the time of when this information was assembled. |
| contractTreeRoot | Field | Root of the contract tree at the time of when this information was assembled. |
| l1ToL2MessagesTreeRoot | Field | Root of the L1 to L2 messages tree at the time of when this information was assembled. |
| blocksTreeRoot | Field | Root of the historic blocks tree at the time of when this information was assembled. |
| privateKernelVkTreeRoot | Field | Root of the private kernel VK tree at the time of when this information was assembled (future enhancement). |
| publicDataTreeRoot | Field | Current public state tree hash. |
| globalVariablesHash | Field | Previous globals hash, this value is used to recalculate the block hash. |

### Public call request

Each _public call request_ is the preimage of a public call stack item in the transaction's `data`, and has the following fields:

| Field | Type | Description |
|----------|----------|----------|
| contractAddress | AztecAddress | Address of the contract on which the function is invoked. |
| callContext | CallContext | Includes function selector and caller. |
| args | Field[] | Arguments to the function call. |
| sideEffectCounter | number? | Optional counter for ordering side effects of this function call. |

### Extended contract data

Each _extended contract data_ corresponds to a contract being deployed by the transaction, and has the following fields:

| Field | Type | Description |
|----------|----------|----------|
| address | AztecAddress | Address where the contract is to be deployed. |
| portalAddress | EthereumAddress | Portal address on L1 for this contract (zero if none). |
| bytecode | Buffer | Encoded Brillig bytecode for all public functions in the contract. |
| publicKey | PublicKey | Master public encryption key for this contract (zero if none). |
| partialAddress | Field | Hash of the constructor arguments, salt, and bytecode. |

## Transaction hash

A transaction is identified by its _transaction hash_. In order to be able to identify a transaction before it has been locally executed, the hash is computed from its [_transaction execution request_](./local-execution.md#execution-request) by hashing:

- `origin`
- `functionSelector`
- `argsHash`
- `txContent`

The resulting transaction hash is always emitted during local execution as the first nullifier of the transaction, in order to prevent replay attacks. This is enforced by the private kernel circuit.
17 changes: 17 additions & 0 deletions yellow-paper/docs/transactions/validity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Validity conditions

The _validity conditions_ of a transaction define when a [_transaction object_](./tx-object.md) is valid. Nodes should check the validity of a transaction when they receive it either directly or through the p2p pool, and if they found it invalid, should drop it immediately and not broadcast it.

In addition to being well-formed, the transaction object needs to pass the following checks:

- **Proof is valid**: The `proof` for the given public `data` should be valid according to a protocol-wide verification key for the final private kernel circuit.
- **No double-spends**: No `nullifier` in the transaction `data` should be already present in the nullifier tree.
- **No pending private function calls**: The `data` private call stack should be empty.
- **Valid historic data**: The tree roots in the historic block data of `data` must match the tree roots of a block in the chain.
- **Preimages must match commitments in `data`**: The expanded fields in the transaction object should match the commitments (hashes) to them in the public `data`.
- The `encryptedLogs` should match the `encryptedLogsHash` and `encryptedLogPreimagesLength` in the transaction `data`.
- The `unencryptedLogs` should match the `unencryptedLogsHash` and `unencryptedLogPreimagesLength` in the transaction `data`.
- Each public call stack item in the transaction `data` should have a corresponding preimage in the `enqueuedPublicFunctionCalls`.
- Each new contract data in transaction `data` should have a corresponding preimage in the `newContracts`.

Note that all checks but the last one are enforced by the base rollup circuit when the transaction is included in a block.

0 comments on commit e6d713d

Please sign in to comment.