The pages before this one described what a transaction does. This page describes what it is made of: a small set of nested objects, each with a fixed list of fields.
Four objects, nested
A transaction is not a flat record. It is one object holding two arrays, and the entries in those arrays hold objects of their own.
Transaction
├─ version, lockTime, subnetworkId, gas, payload
├─ inputs[]
│ └─ Input
│ ├─ previousOutpoint → { transactionId, index }
│ ├─ signatureScript
│ ├─ sequence
│ └─ computeCommit
└─ outputs[]
└─ Output
├─ value
├─ scriptPublicKey → { version, script }
└─ covenant → { authorizingInput, covenantId } (optional) This is the whole model. Everything a wallet displays that is not in this tree - the fee, the sender, the amount an input is worth, the balance - is either computed from it or fetched from node state.
The transaction
Seven fields wrap the two arrays.
| Field | Holds | Why it exists |
|---|---|---|
version | 0 or 1 | Selects which field set inputs and outputs use. See below. |
inputs | array of Input | The coins being spent. A non-coinbase transaction with none is rejected outright. |
outputs | array of Output | The coins being created. |
lockTime | u64 | The earliest point the transaction may be included. Zero means immediately. |
subnetworkId | 20 bytes | All zeros for an ordinary transaction. A reserved value marks the coinbase. |
gas | u64 | Zero for an ordinary transaction. Carries the gas commitment on a subnetwork transaction. |
payload | bytes | Arbitrary data, empty by default, opened to ordinary transactions at Crescendo. |
A plain payment sets three of them: version zero, the inputs, and the outputs. Lock time stays zero, the subnetwork id stays all zeros, gas stays zero, and the payload stays empty.
Lock time encodes two meanings in one field. Below five hundred billion the value is read as a minimum DAA score; at or above it, as a Unix timestamp in milliseconds. The threshold is far past any block count the network will reach and far before any plausible timestamp, so the two ranges cannot collide.
An input names a coin, it does not contain it
| Field | Holds | Why it exists |
|---|---|---|
previousOutpoint | outpoint | Which coin is being spent. |
signatureScript | bytes | The proof that spending it is allowed - signatures, and for pay-to-script-hash the redeem script too. |
sequence | u64 | Relative lock time, and the finality flag. See below. |
computeCommit | enum | How much script execution this input is paying for. |
The outpoint is two fields and nothing more:
| Field | Holds |
|---|---|
transactionId | 32 bytes - the transaction that created the coin |
index | u32 - which of that transaction’s outputs it was |
The amount is absent. An input identifies a coin; it does not state its value. The value being spent appears nowhere in the transaction, so a transaction cannot be validated on its own - the coin must be looked up.
Sequence carries two meanings in one number. The top bit is a disable flag; when it is set, the input has no relative lock. When it is clear, the low thirty-two bits are a number of blocks that must have passed since the coin’s own DAA score before the input may be spent. Separately, if every input is set to the maximum value - which has that top bit set - the transaction counts as final and its lock time no longer holds it back.
The compute commitment is one of two variants, and the transaction version decides which:
| Variant | Type | Used by |
|---|---|---|
sigOpCount | u8 | Version 0 |
computeBudget | u16 | Version 1 |
Both resolve to the same thing - an allowance of script units for that input, on top of a small free budget every input gets. A sigop count states how many signatures the input checks. A compute budget prices arbitrary execution directly, which is what a covenant or a proof verification needs.
An output is an amount and a lock
| Field | Holds | Why it exists |
|---|---|---|
value | u64 | The amount, in sompi. Integers only; KAS is a display unit. |
scriptPublicKey | struct | The condition for spending the coin next. |
covenant | optional | The output’s covenant binding. Version 1 only. |
The locking script is versioned, though only one version exists so far:
| Field | Holds |
|---|---|
version | u16 - zero is the only value consensus accepts today |
script | bytes - decoded from the recipient’s address |
And a covenant binding, when present, is also two fields:
| Field | Holds |
|---|---|
authorizingInput | u16 - the index of the input that permitted this output |
covenantId | 32 bytes - the lineage the output belongs to |
The lock types in circulation are charted at output script classes.
The coin record a node supplies
Because an input is only a pointer, validating a transaction means pairing every input with the coin it names. That coin is its own object, held in the node’s UTXO set rather than in the transaction:
| Field | Holds | Used for |
|---|---|---|
amount | u64 | The value being spent. This is where the fee arithmetic gets its inputs. |
scriptPublicKey | struct | The lock the signature script has to satisfy. |
blockDaaScore | u64 | When the coin was created - the baseline for relative locks and for maturity. |
isCoinbase | bool | Whether the longer coinbase maturity rule applies. |
covenantId | optional | The lineage the coin carries, checked against the covenant rules on spend. |
Validation pairs a transaction with node state. The transaction supplies pointers and proofs, the node supplies the coins, and the rules run against the combination.
Two practical consequences follow. A wallet cannot build a transaction without first fetching its own coins - that is what getUtxosByAddresses is for, and why the node serving it needs the UTXO index enabled. And a node that has never seen the parent transaction cannot validate the child, which is the situation the orphan pool exists to hold.
What is computed rather than stored
| Value | Where it comes from |
|---|---|
| Transaction id | A hash over the transaction with signature scripts and payload left out, so the id is settled before anyone signs. |
| Transaction hash | A hash over everything, signatures included. |
| Fee | Total input value minus total output value. See Fees & Mass. |
| Compute mass | Derived from the size and the compute commitments. |
| Transient mass | Derived from serialized size. |
| Storage mass | Derived from the input and output amounts. |
Storage mass is the one exception. The transaction carries a commitment to it, and consensus recalculates the figure and rejects the transaction if the two disagree - so it is stored and computed, and the stored copy is a claim that consensus checks. The SDKs fill it in automatically.
The two versions
| Version | Enables |
|---|---|
0 | The original layout. Inputs commit a sigop count; outputs have no covenant field. |
1 | Covenant bindings on outputs, compute budgets on inputs, non-native subnetworks, gas commitments. Arrived with Toccata. |
The two cannot be mixed. A version 0 transaction cannot carry a covenant binding, and a version 1 transaction commits compute per input rather than counting signature operations. Version 0 remains valid indefinitely; an ordinary payment does not need version 1.
See Also: UTXO Model, Fees & Mass