sommaire · 7 sections
In the previous article we looked at what Bitcoin signs with: an elliptic curve, a hash function, a key pair. That left the question that actually matters: what exactly is being signed?
The answer fits in an object smaller than a holiday photo. The transaction we’re taking apart here weighs 222 bytes. It was confirmed on 23 August 2026 in block 963,721, it is public, and by the end of this article you will be able to read every one of its bytes. Inside we’ll find the UTXO model, a miniature programming language, four different ways to lock the very same amount, and a flaw fixed in 2017 that made the Lightning Network buildable.
Bitcoin does not know your balance
First thing to unlearn: nowhere in Bitcoin is there a line saying “Nicolas: 1.45 BTC”. No account table, no balance field. What the blockchain holds is a set of unspent transaction outputs — UTXOs.
The analogy that works is the physical wallet. You don’t have “$47” in your pocket: you have a $20 note, a $10, two $5s, and some coins. When you pay $23, you don’t cut a note in half — you hand over the $20 and the $10, and you get $7 back. A UTXO is exactly that note: an indivisible amount, locked behind a condition, spendable only in full.
What your wallet displays as a “balance” is therefore a sum it computes itself, by scanning the chain for every UTXO it knows how to unlock. The protocol never performs that addition.
The alternative exists and dominates elsewhere: the account model, as used by Ethereum. There, a transfer decrements one field and increments another. It is more intuitive for a human, more compact to store, and far more natural for a smart contract that has to maintain state.
So why did Satoshi go the other way? Three reasons still hold today:
- Parallelisable validation. Two transactions touching different UTXOs are entirely independent: a node can check them at the same time, in any order. With accounts you have to serialise — otherwise two simultaneous debits on the same balance step on each other.
- No global mutable state. Verifying a spend means answering a local question (“does this UTXO exist, and is its condition met?”), not querying a ledger that everyone is constantly rewriting.
- Natural atomicity. A transaction consumes its inputs and creates its outputs in one shot. There is no intermediate state where money has left A without having reached B.
The price is real too. A wallet has to track each of its UTXOs individually, and fees depend on the number of notes you spend, not on the amount. Paying $10 with fifty 20-cent coins costs more than paying it with a note — hence the practice of consolidation, where you merge your small UTXOs during low-fee periods. And above all: every payment creates a change UTXO that comes back to you and leaves an exploitable trace. More on that in Privacy on Bitcoin (BTC-A-07, coming in this series).
222 bytes, read one by one
Enough theory. Here is the transaction as it travels across the network — 444 hexadecimal characters, i.e. 222 bytes:
02000000000101cfae86692a46f47926ed39137eef6477918cec8aa12860bdb2e12be57376c565
0100000000fdffffff02002e020000000000160014f3795ce71ad1ba973e90ed089830c0fe6664
852072d827000000000016001481562ca2cdf67e6906b71506c164e3118ee11cd2024730440220
4e1ff4e377943af919bc88c6d10fe46939856770dea1b6a25d763fd377b1ae67022058895a90f4
7b92140b9d96bad430d51e8ae564792b8185dedef75a1c84c4f55f012103e6332b27432156eb98
3facb9c478deffcaead4c337d53cedf408fe5b6f7322b36eb40e00
This is not a teaching example cooked up for the occasion: it is transaction b0d246e7…5353c72f, mined in block 963,721.1 It spends one UTXO and creates two. Here’s how to slice it.
A few fields deserve a comment.
02000000 — the version. Four bytes, little-endian (least significant byte first), so the value is 2, not 33,554,432. Internalise this once and for all: in a Bitcoin transaction every integer is little-endian, including transaction identifiers, which we nonetheless display back-to-front. It is the number-one mistake of anyone writing their first parser.
00 01 — the marker and the flag. Two bytes that did not exist before 2017. The 00 sits where the input count belongs: since a transaction with zero inputs is impossible, an old node reading 00 concludes “nothing to see here” and stops — which is precisely the sleight of hand that allowed the witness to be added without a hard-fork. A modern node reads the 01 behind it and knows it is dealing with a SegWit transaction.
01 then 02 — the counters. These are varint — variable-length integers: below 253 the number fits in a single byte; above that, a prefix (fd, fe, ff) announces 2, 4 or 8 bytes. A 3-input transaction and a 300-input transaction therefore don’t spend the same number of bytes saying how many they have. This is byte-level optimisation, and Bitcoin is full of it.
cfae8669…7376c565 — the reference to the spent UTXO. Thirty-two bytes pointing at the transaction the money came from, followed by four bytes of index (01000000 = output number 1 of that transaction). This is the only way to designate money in Bitcoin: by its origin. Not by an address, not by a balance — by a (transaction, index) pair.
00 — the scriptSig length. Zero. The historical field where the signature used to go is empty, because this is a SegWit transaction: the signature has moved further down, into the witness. We’re getting there.
fdffffff — the nSequence. A field with a turbulent history. Today its value 0xfffffffd does two things: it enables nLockTime (any value below 0xffffffff does) and it signals opt-in RBF (BIP125
), which allows the transaction to be replaced by a better-paying version while unconfirmed. One nuance: since Bitcoin Core 28 (October 2024), replacement is relayed by default, signal or no signal — the field stayed, its reach changed.2
002e020000000000 — a value. Eight bytes, little-endian again, denominated in Satoshi: 142,848 sat, i.e. 0.00142848 BTC. There is no decimal number anywhere in Bitcoin. Ever. “BTC” is a display convention, nothing more.
6eb40e00 — the nLockTime. 963,694, a block height — already in the past by the time the transaction confirms, so it blocks nothing. That is not a mistake: Bitcoin Core sets the locktime to the current height by default, a technique called anti-fee-sniping. The idea: if a miner were tempted to reorganise the last block to grab its fat transactions, those transactions would not yet be valid in the block it is trying to replace.
And the total? 142,848 + 2,611,314 = 2,754,162 sat going out, against 2,754,952 sat coming in. The difference — 790 satoshis — is written nowhere. It doesn’t need to be: in Bitcoin, the fee is whatever is left over. Anything you fail to assign to an output goes to the miner who includes the transaction. A buggy wallet that forgets its change output therefore gives the remainder away, and that has happened.
Script: the language of locks
Every output carries a spending condition. That condition is not an address type or a flag: it is a small program, written in a language specific to Bitcoin, called Script.
Script is stack-based: it has no variables, only a stack. Each instruction — an opcode — pops its arguments and pushes its result. And crucially, it is not Turing-complete: no loops, no backward jumps, no recursion. That is not a shortcoming, it’s a design decision. A Bitcoin script always terminates, in a number of steps bounded by its own length, which makes the cost of validating it predictable before you even run it. Ethereum made the opposite choice — Turing-completeness — and had to invent gas to stop infinite loops from freezing the network.
Unlocking happens in two parts. The output you want to spend carries a scriptPubKey (the lock). The transaction spending it supplies a scriptSig or a witness (the key). You concatenate the two, run them, and if the stack ends on a true value, the spend is authorised.
Here is the textbook case, P2PKH — the original 2009 form:
scriptPubKey OP_DUP OP_HASH160 <20-byte hash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig <signature> <public key>
Six opcodes are enough to understand 90% of what’s out there:
| Opcode | What it does |
|---|---|
OP_DUP | duplicates the item on top of the stack |
OP_HASH160 | replaces the top with its RIPEMD-160(SHA-256(x)) |
OP_EQUALVERIFY | compares the top two items, pops them, fails if they differ |
OP_CHECKSIG | checks a signature against a public key, pushes true/false |
OP_RETURN | makes the output permanently unspendable (see below) |
OP_IF / OP_ELSE / OP_ENDIF | conditional branching — the language’s only control structure |
Two notes on the limits. First, Script sees nothing of the outside world: not the wall-clock date, not a price, not the contents of another transaction. It can only reason about what is pushed onto its stack and a few block-level facts. Second, part of the language was disabled in 2010 by Satoshi himself — including OP_CAT, which concatenated two items — because of bugs and denial-of-service risks in their implementation. Those opcodes still exist in the code but make any script invoking them fail. Whether they should come back is one of today’s big debates, which we’ll reopen at the end of the series.
Four families of locks
The same amount can be locked in four different ways, stacked up by fifteen years of protocol evolution. Each remains valid: Bitcoin has never retired a family already in circulation.
P2PKH (Pay to Public Key Hash, 2009) — the whole lock is written in the output: duplicate, hash, compare, check the signature. Simple, readable, and the most expensive of the lot: 226 vB for a one-input, two-output transaction. Addresses start with 1.
P2SH (Pay to Script Hash, BIP16
, activated April 2012) — the conceptual flip. The output no longer holds the script, only its fingerprint. The real script, called the redeem script, is revealed only at spending time, by whoever spends. The practical consequence is enormous: the payer no longer has to know anything. Sending to a 2-of-3 multisig becomes as simple as sending to a normal address — the complexity moves to the receiving side. Addresses start with 3.
P2WPKH (SegWit v0, BIP141
, activated August 2017) — the output shrinks to OP_0 <20-byte hash>, and the signature moves into the witness. The gain is not cosmetic: witness data is billed four times less than the rest (see the box). Addresses bc1q…, like the ones in our example transaction. 141 vB.
P2TR (Taproot, BIP341
, activated November 2021) — the output is OP_1 <32-byte public key>, and that key is a tweaked one: it encodes both “spend with a plain signature” and, hidden inside it, a tree of alternative scripts. If you spend the simple way, nobody ever learns the other path existed. That’s the subject of Advanced scripts and Taproot (BTC-A-09, coming). Addresses bc1p….
The witness, and the flaw it closed
That leaves the part of the transaction we set aside: the 109 bytes of marker, flag and witness. To see why they’re there, we have to revisit a defect that haunted Bitcoin for eight years.
Before 2017, the signature lived in the scriptSig, inside the body of the transaction — and therefore hashed into the txid. But an ECDSA signature admits several valid encodings of the same mathematical signature: you can change a few bytes of its representation without invalidating it, and without holding the private key. Any node along the path could therefore grab a transaction in flight, alter its encoding and rebroadcast it: same effect, same amounts, same recipient… but a different identifier. That is transaction malleability.
The damage isn’t theft — malleability never let anyone divert a single satoshi. The damage is that the txid became an unreliable anchor. And every protocol built on top of Bitcoin needs to pre-sign transactions that reference other, not-yet-confirmed ones. A Lightning channel, for instance, relies on a refund transaction signed before the funding transaction is even in a block. If the funding txid can shift under your feet, the refund points at nothing and the money is stuck. Lightning was impossible to build while malleability existed.
SegWit fixes the problem by relocation rather than repair: the signatures are taken out of the txid computation. The witness is serialised after the outputs, and the txid is computed on the transaction as if the witness did not exist. Altering a signature therefore no longer changes the identifier. To identify the complete transaction, witness included, SegWit introduces a second identifier, the wtxid — used for network relay and for a dedicated Merkle tree inside the block.
On our example, the two values are indeed different:
txid b0d246e7e26433a81ddac344a0ba2240a0a7d06264de169c960d8d7d5353c72f
wtxid c7377a55d130023b9a8448d0662f8fe9347babcaa3dde307d758bd8e9985fa11
A word on a stubborn legend. In February 2014, Mt. Gox halted withdrawals and publicly blamed malleability. The academic analysis published shortly after by Decker and Wattenhofer measured, over more than a year of network traces, 1,811 bitcoins involved in malleability attacks before withdrawals stopped — nowhere near the hundreds of thousands missing — and an explosion of the phenomenon after the announcement, once the trick had been made public.3 Malleability was a genuine protocol problem; it did not empty Mt. Gox.
What to take away
- Bitcoin stores no balances, only UTXOs: indivisible amounts locked by a script, spendable only in full.
- A transaction is a compact binary structure: version, inputs, outputs, witness, locktime. Every integer is little-endian, every amount is in satoshis.
- The lock is a program written in Script, deliberately not Turing-complete: bounded validation cost, no loops, no access to the outside world.
- Four output families coexist — P2PKH, P2SH, P2WPKH, P2TR — from 2009 to 2021, with very different costs, and Taproot is not systematically the cheapest.
- SegWit took signatures out of the txid, settling malleability and making Lightning buildable, while introducing a weight system that discounts witness data by a factor of 4.
- Fees are written nowhere: they are the difference between what goes in and what comes out.
What’s next
We now know what a transaction looks like, and how a node decides it is valid. What’s left is the question of when: how those 222 bytes end up in a block, who decides to put them there, and why electricity has to be burned for it.
That’s the subject of BTC-A-03 — Bitcoin mining, in detail (coming): the 80-byte header, the nonce, difficulty and its adjustment every 2,016 blocks, and the very concrete economics of compute farms.
This article is not investment advice.
Transaction
b0d246e7e26433a81ddac344a0ba2240a0a7d06264de169c960d8d7d5353c72f, confirmed in block 963,721 on 23 August 2026. Raw data pulled from the public mempool.space API ; the txid, wtxid, weight and virtual size quoted in this article were recomputed independently from the 222 bytes. ↩︎The Bitcoin Core 28.0 release notes (October 2024) record the
-mempoolfullrbfoption default moving from0to1, making replacement possible even without BIP125 signalling. ↩︎Christian Decker and Roger Wattenhofer, Bitcoin Transaction Malleability and MtGox , ESORICS 2014. The authors count 1,811.58 BTC involved in malleability attacks before Mt. Gox stopped withdrawals (February 2014), out of a total of 302,700 BTC involved across the whole observed period — the overwhelming majority therefore coming after the announcement. ↩︎