Anatomy of a Transaction
A Bitcoin transaction is, at heart, a short, rigid data structure that says: “consume these existing coins, and create these new ones.” Nothing more. There is no “from” address, no “amount sent” field, no memo. Everything you think of as a payment is reconstructed from this skeleton. Once you can read the skeleton, raw transaction hex stops being intimidating — it’s just a handful of labelled fields.
The five top-level fields
Section titled “The five top-level fields”TRANSACTION├── version : which consensus rules this tx expects (usually 1 or 2)├── inputs[] : the coins being SPENT (each points at a prior output)├── outputs[] : the coins being CREATED (value + lock)├── witness : signatures/scripts for SegWit inputs (separated since 2017)└── locktime : "not valid until" block height or timestamp (often 0)Two of these — version and locktime — are single scalars wrapping the transaction. The action is
in the two lists: inputs (what’s consumed) and outputs (what’s made). Let’s open each.
Inputs — references to coins you’re spending
Section titled “Inputs — references to coins you’re spending”An input does not contain money. It contains a pointer to an output created by some earlier transaction, plus the proof that you’re allowed to spend it.
INPUT├── previous output│ ├── txid : the id of the transaction that created the coin│ └── vout : which output of that tx (the index, 0-based)├── scriptSig : the unlocking script (legacy) — the "key" to the lock├── nSequence : a 4-byte field: RBF signaling + relative timelocks└── (witness) : the unlocking data for SegWit inputs, stored separatelyThe pair (txid, vout) is an outpoint — it uniquely names exactly one UTXO in the whole
ledger. By referencing it, the input says “I am spending that specific coin.” A validating node
looks the outpoint up in the UTXO set; if it’s missing
(already spent, or never existed), the transaction is invalid. This is the double-spend defense in
action.
The scriptSig (and, for modern inputs, the witness) is the satisfaction of the prior
output’s lock — typically a signature and a public key. We dissect locking vs unlocking on
its own page.
nSequence is a 4-byte field with a tangled history: it now does double duty as the
(historically opt-in) RBF signal and as the relative
timelock mechanism (BIP 68).
Outputs — the new coins you’re creating
Section titled “Outputs — the new coins you’re creating”An output is the simplest object in Bitcoin: an amount and a lock.
OUTPUT├── value : amount in SATOSHIS (integer; 1 BTC = 100,000,000 sats)└── scriptPubKey : the locking script — the spending conditionBitcoin stores no fractional float for value — everything is an integer count of satoshis, which
avoids rounding ambiguity entirely (every node computes the exact same number). The
scriptPubKey is the cryptographic lock placed on the coin: usually “spendable by whoever can
produce a signature for public key K.” When this output is later spent, it becomes some future
input’s referenced outpoint.
The whole thing, labelled
Section titled “The whole thing, labelled”Here is a typical 2-input, 2-output payment, drawn as one structure:
tx (version 2)│├─ inputs│ ├─ in[0] prevout = (txid_A, 1) scriptSig/witness = <sig><pubkey>│ └─ in[1] prevout = (txid_B, 0) scriptSig/witness = <sig><pubkey>│├─ outputs│ ├─ out[0] value = 50,000,000 sat scriptPubKey = <lock to recipient>│ └─ out[1] value = 24,900,000 sat scriptPubKey = <lock back to YOU (change)>│└─ locktime = 0Read it as a sentence: “Using consensus rules v2, consume coin (txid_A,1) and coin (txid_B,0) — here are the keys — and create a 0.5 BTC coin for the recipient and a ~0.249 BTC coin for myself; no time restriction.” The fee is whatever the inputs total minus the outputs total — it is never written down explicitly.
locktime and version, briefly
Section titled “locktime and version, briefly”versionsignals which validation rules apply. Version 2 is required to use BIP 68 relative timelocks; otherwise the difference is mostly historical.locktimelets a transaction declare itself invalid until a given block height or Unix time. Even ordinary wallets set it to roughly the current height as an anti-fee-sniping measure (see Timelocks & HTLCs).
Under the hood — how the five fields become bytes on the wire
Section titled “Under the hood — how the five fields become bytes on the wire”The five fields don’t travel as a tree — on the wire a transaction is a flat, length-prefixed byte string in a fixed order. Here is the layout a node parses, left to right:
field bytes─────────────────────────────────────────────version 4 (little-endian)[marker 0x00] 1 ┐ present only for SegWit txs[flag 0x01] 1 ┘ (BIP 144)input count 1+ (CompactSize varint) per input: txid 32 (internal byte order) vout 4 (little-endian) scriptSig 1+ length prefix, then the bytes sequence 4output count 1+ (CompactSize varint) per output: value 8 (little-endian, satoshis) scriptPubKey 1+ length prefix, then the bytes[witness] one stack of items per input (SegWit only)locktime 4A few choices make this parseable with zero ambiguity. A CompactSize varint before each list
says how many items follow, so a reader always knows when a list ends; every variable-length script
carries its own length prefix. And for SegWit transactions a 0x00 marker plus a 0x01 flag
sit right after the version — an old parser reads that marker as “zero inputs” and stops, which is
exactly how new and old software tell the two formats apart (BIP 144). The witness stacks then ride
between the outputs and the locktime.
Note the 32-byte txid inside each input is stored in Bitcoin’s internal byte order — the reverse of what a block explorer prints. That quirk gets its own treatment in transaction IDs.
The thread
Section titled “The thread”How does this rigid structure help untrusting strangers agree on one ledger? Because there is nothing to interpret. Every node parses the exact same fields, looks up the exact same outpoints, checks the exact same scripts, and sums the exact same integers. A payment isn’t a claim that must be believed — it’s a structure that is either well-formed and spends real, unspent coins with valid proofs, or it isn’t. The format’s rigidity is the agreement: there’s no room for two honest nodes to read the same transaction differently.
The architect’s lens
Section titled “The architect’s lens”Step back from the five fields and answer the five questions an architect asks of any wire format:
- Why does it exist? To encode “consume these coins, create these new ones” as a rigid, unambiguous data structure — version, inputs, outputs, witness, locktime — that every node parses byte-for-byte identically, with nothing left to interpret.
- What problem does it solve? Universal agreement on what a payment is: CompactSize varints and length-prefixed scripts let any reader find where each list ends, and integer-satoshi values (no floats) mean every node computes the exact same number — no rounding disputes.
- What are the trade-offs? Spartan rigidity — no “from” field, no “amount sent”, no memo; a payment is reconstructed from inputs and outputs. Inputs spend coins whole, forcing a change output, and the fee is never written down — it’s the gap between input and output totals.
- When is it the wrong design? An account-model transaction carries an explicit sender, recipient, amount, and per-account nonce instead of outpoint references — more legible and stateful, but it trades away UTXO’s parallel, set-membership validation.
- What breaks if I remove it? Drop the
(txid, vout)outpoint and an input can’t name which coin it spends — the double-spend defense (look the outpoint up in the UTXO set, reject if missing) disappears. Fold the witness back intoscriptSigand txids become malleable again, the very flaw SegWit’s separate witness field fixed.
Check your understanding
Section titled “Check your understanding”- List the five top-level fields of a transaction and say in one phrase what each does.
- What does an input actually contain — and why does it hold no money itself?
- What is an outpoint
(txid, vout), and what does a node do with it during validation? - Why are output values stored as integer satoshis rather than decimal BTC?
- Why was witness data separated from
scriptSig, and what problem did that solve?
Show answers
versionsignals which consensus rules apply;inputs[]are the coins being spent (each points at a prior output);outputs[]are the coins being created (value + lock);witnessholds signatures/scripts for SegWit inputs;locktimedeclares a “not valid until” height or time.- An input contains a pointer — the
(txid, vout)outpoint of an earlier output — plus the proof (scriptSig/witness) that you may spend it. It holds no money itself because value lives in outputs; the input merely references a coin created elsewhere. - An outpoint
(txid, vout)uniquely names exactly one UTXO in the whole ledger. During validation a node looks it up in the UTXO set; if it’s missing (already spent or never existed) the transaction is invalid — this is the double-spend defense in action. - Everything is an integer count of satoshis (1 BTC = 100,000,000 sats), which avoids rounding/float ambiguity entirely so every node computes the exact same number — essential when mutually distrusting nodes must reach an identical verdict.
- Before 2017 signatures lived in
scriptSig, which was hashed into the txid, so a third party could re-encode a signature and change the id without invalidating it — transaction malleability. SegWit moved signatures into a separate witness field excluded from the legacy txid, fixing malleability and unlocking Lightning.