This section gives an overview of the structure, design and motivation of the Radix transaction model. If you are an integrator looking to build and submit transactions, we encourage you to read the Transactions for Integrators.
User Transactions
User Transactions are also known as "Notarized Transactions". At a high level, a user transaction contains:
- A transaction intent, and related signatures, including the signature of a notary.
- Zero or more subintents, each with their related signatures.
Together, the transaction intents and subintents are known as the intents of a transaction. Each intent contains an intent header, a message, and a manifest which details a human-readable set of commands that will be executed.
Structure
There are two structures to a transaction, discussed in separate articles:
- The tree-based intent structure, with the transaction intent at the root, with sub-trees of subintents below. This structure is useful when thinking about how transactions execute, and when building transactions.
- The serialized transaction structure where the subintents are flattened for easy serialization and for referencing by subintent index. This structure is useful for those debugging, or working on transaction parsers.
Ledger Transactions
Transactions are committed by a node as a Ledger Transaction (definition in code). They capture three classes of transaction:
- User Transactions (definition in code)
NotarizedTransactionV1
was released at Babylon, and only has support for a single transaction intent.NotarizedTransactionV2
was released at Cuttlefish and added support for subintents, timestamp-based validity and tip specification in basis points.
- Protocol Update Transactions
- "Flash" state updates
- Executable transactions
- Validator Transactions
- Round update transactions, which are typically short/simple transactions, but occasionally trigger epoch updates about every 5 minutes
Each ledger transaction has an associated LedgerTransactionHash
, built as a hash on top of the hash of the transaction itself.
Serialization and Preparation
Transactions have a canonical serialization in bytes, using ManifestSbor, according to the AnyTransaction serialization.
In the Rust library, you can encounter a transaction in a few forms:
- Under creation, in a transaction builder, e.g.
TransactionBuilder::new_v2()
- Its detailed model, e.g.
DetailedNotarizedTransactionV2
created from the builder - Its normal model, e.g.
NotarizedTransactionV2
- Its raw bytes, e.g.
RawUserTransaction
- a wrapper around the canonical serialization of the transaction - Its prepared form, e.g.
PreparedUserTransaction
orPreparedNotarizedTransactionV2
- Its validated form, e.g.
ValidatedUserTransaction
orValidatedNotarizedTransactionV2
- Its executable form, e.g.
ExecutableTransaction
In order to get the hashes of a transaction, it must be prepared.
Executable Transactions
When converted into an Executable, it includes:
- Configuration details, such as tip and costing details.
- Details for Transaction Validation:
- The transaction and subintent hashes, for replay prevention
- The combined min/max epoch and min/max timestamp across all intents in the transaction
- Execution details for the transaction intent and each subintent:
- Implicit Proofs to add to the intent processor's authorization zone when it's created, e.g. from Signatures.
- The intent's manifest (blobs, instructions e.t.c) which are sent to the Intent Processor.