txlab.cash
// Transaction Lab · the life of a transaction
// The Transaction Lab · six stages · one real transaction

The life of a transaction.

Every Bitcoin Cash payment is a small object with a short, eventful life. This one is going to tell you about it in first person. Everything you will see is real: the keys, the signature, and the script check are computed by Libauth, the same code wallets use. Only the two coins it starts with are invented, because a demo wallet has no past.

mainnet only · fetched from public Electrum servers · nothing is stored ·try an example
▼ scroll to begin · switch lenses any time
THE WALLETWHERE TRANSACTIONS ARE BORN · OUTSIDE THE NODEthe node · 1 hop awayYOUR UTXOs30,00017,500PAYMENT42,000 → merchantCHANGE5,100 → youSIGHASH_ALL | FORKIDTO THE NETWORK →THE NETWORKGOSSIP · EVERY NODE TELLS ITS PEERS · NOBODY IN CHARGEyour walletyouyour peerstheir peersthe whole network · ~1 sBITCOIN CASH FULL NODEBCHN · peers: 14THE VALIDATION GAUNTLETFIVE CHECKS · EVERY NODE RUNS ALL OF THEM · ON EVERY TRANSACTION1 · WELL-FORMED360 bytes · v2 · 2 in · 2 out✓…3c4b:1 · 30,000…e7f2:0 · 17,5002 · INPUTS EXIST · UTXO SET✓3 · SCRIPT✓STACK MACHINESCRIPTSTACK · top last400 sats360 B4 · FEE1.11 sat/byte · min 1.0✓5 · FIRST SEENinputs unspent · no conflict✓⚠ DSPROOF BROADCASTsame coin · spent twiceMEMPOOL →THE MEMPOOLEVERY VALID TX FLOATS HERE UNTIL A BLOCK COMES · FIRST SEEN · NO FEE AUCTION1f3c…09a2c07e…5d185b90…e7c4e921…33b07d2a…f1e6POOL RULES1. FIRST SEEN, FIRST SERVED2. NO REPLACEMENTS (NO RBF)3. EVERYONE GETS IN · 32 MB+ BLOCKS4. ~1 sat/byte. ALWAYS.SINCE LAST BLOCKavg 10 min · never on scheduleTO THE BLOCK ↓THE BLOCKA MINER FOUND IT · OUR NODE STILL CHECKS EVERYTHING · CANONICAL ORDERBLOCK HEADER · 80 BYTES#966,240prev000000000000000000f3…9c1amerkle7ab2…4e01 (every txid, folded)time2026-09-28 17:48:02 UTCbitstarget · ~2^71 hashes expectednonce2c9e1b07✓hash below target · proof of work validCANONICAL ORDER · SORTED BY TXID · COINBASE FIRSTe921…33b07d2a…f1e61f3c…09a2c07e…5d185b90…e7c4COINBASE3.125 BCH + all fees→ the miner · spendable in 100 blocks#966,2401 CONFIRMATIONTHE ARCHIVETHE CHAIN · KEPT BY EVERY NODE · THERE IS NO UNDO#966,236#966,237#966,238#966,239#966,2401 CONFIRMATION2 CONFIRMATIONS6 CONFIRMATIONS · SETTLED10 deep: BCHN will never reorganize it away#966,241#966,242#966,243#966,244#966,245THE MERCHANT'S WALLET42,000 · a44d3a…:0YOUR WALLET5,100 · a44d3a…:1NEW UTXOs · THE CYCLE CONTINUES ↺✓txid
raw tx · 360 bytes ·
Stage 1 · The wallet

I am born empty.Every transaction starts in a wallet, outside the network. Nobody has heard of me yet.

Right now I am nothing but an intention: send 42,000 satoshis to the coffee shop. I have no coins in me, no signature, no name. A wallet is about to build me, piece by piece.

A transaction is a data structure with four parts: a version, a list of inputs, a list of outputs, and a locktime. The wallet fills them in locally. No node, no server, and no permission are involved at this point.

The serialized form starts with a 4-byte little-endian version and ends with a 4-byte locktime. Everything in between is what the next steps write.

02000000 … inputs … outputs … 00000000
Stage 1 · The wallet

Coins are poured in. Whole.

The wallet picks two coins from the shelf, one worth 30,000 sats and one worth 17,500, and pours both into me entirely. Coins are never edited or partially spent. They are consumed, and new ones are made.

The coins are UTXOs: unspent transaction outputs. Each input references one by the id of the transaction that created it and the output index. The input does not carry the amount; the node will look that up in its own UTXO set.

Each outpoint is the 32-byte txid (byte-reversed on the wire) plus a 4-byte index. The highlighted bytes below are the outpoints.

Stage 1 · The wallet

New coins are written. Not yet born.

Now the wallet writes down what should come out of me: 42,000 sats for the coffee shop, and 5,100 sats of change back to my owner. These coins do not exist yet. They are blueprints. They only become real if I make it all the way to a block.

An output is an amount and a locking script: "whoever can satisfy this script may spend it". A change output goes back to the owner's own address. The gap between inputs and outputs is the fee, which no output claims.

Each output is an 8-byte little-endian amount, a script length, and the locking script; a P2PKH script is the 25 bytes 76a914 <20-byte hash> 88ac.

Stage 1 · The wallet

Signed and sealed.

The private key never leaves the wallet. It stamps me instead: a signature that covers all of me, inputs and outputs alike. Change one byte of me later and the stamp no longer fits. The stamp also says Bitcoin Cash only, so I am worthless on any other chain.

Each input gets an unlocking script; for a plain spend that is a signature and the public key. The signature commits to the whole transaction using SIGHASH_ALL, and SIGHASH_FORKID mixes the BCH fork id into the digest so the signature cannot be replayed on BTC or any other fork.

A plain unlocking script is two pushes: the signature (Schnorr is 64 bytes, ECDSA 70 to 72, plus the sighash byte) and a 33-byte public key. Highlighted below are the inputs' scripts.

Stage 1 · The wallet

I have a name now.

Hash every byte of me twice and you get my name: my transaction id. It is not assigned by anyone. It falls out of what I am. If any part of me changed, I would be a different transaction with a different name.

The txid is the double SHA-256 of the serialized transaction, displayed byte-reversed. It is computed, not allocated, which is why explorers on different continents agree on it without talking.

All 360 bytes, in wire order. This exact hex is what the wallet is about to hand to the network.

Stage 1 · The wallet

Time to go.

There is nothing more the wallet can do for me. I am complete. Whatever happens next happens out there, and I do not get a say in it. Deep breath.

The wallet hands the raw bytes to one or more peers it is connected to, typically over the P2P protocol or through an Electrum-style server that relays on its behalf. Once relayed, the wallet cannot recall or replace the transaction.

The wire message is a tx payload carrying exactly the bytes from the previous step, wrapped in the standard P2P message header (magic, command, length, checksum).

Stage 2 · The network

Broadcast.No middleman, no queue, no permission.

My wallet tells the handful of nodes it knows. That is all. There is no central place to submit me, and no one to approve me. Whoever hears about me may pass me along.

A node does not blast the full transaction at every peer. It announces the txid with an inv message; peers that have not seen that id reply with getdata, and only then is the tx sent. This keeps duplicate traffic low as the same transaction arrives from many directions.

Inventory announcements carry only the 32-byte hash, so the first hop for most peers is 36 bytes, not 360.

Stage 2 · The network

They tell their peers. Their peers tell theirs.

Each node that hears about me checks me quickly and, if I look right, tells the nodes it knows. Nobody coordinates this. It spreads the way news spreads through a crowd.

Every node validates before relaying, so an invalid transaction dies at the first hop and never floods the network. Relay is best effort: a node may delay or skip an announcement, and a transaction has no guarantee of reaching everyone, only a very high probability.

Each hop repeats the same inv / getdata / tx exchange. The transaction bytes do not change along the way, and neither does the txid.

Stage 2 · The network

About one second later, I am everywhere.

Nearly every node on Earth now knows me by name and holds a copy. If my owner's wallet went offline this instant, it would not matter. I am out of its hands and into everyone's.

Propagation across the public network usually completes within a second or two. Merchants rely on this: a zero-confirmation payment seen by many nodes is very hard to quietly replace, and the network will broadcast a double-spend proof if anyone tries.

Measured propagation on BCH is typically well under two seconds to reach the large majority of reachable nodes. The exact figure varies with topology and load.

Stage 2 · The network

One of those nodes is this one.

Let us follow me into a single full node. It runs software called BCHN, it talks to fourteen peers, and it has just received my bytes. What happens inside is the same in every node that ever sees me.

A full node keeps the entire UTXO set and enforces every consensus rule independently. It does not trust the peer that relayed the transaction. Before the transaction can enter this node's mempool it must pass every check below, in order.

The node has just parsed the tx message off the socket into an in-memory transaction object and is about to run it through AcceptToMemoryPool.

Stage 3 · Inside a full node

Five checks.No exceptions, not even for you.

This is the part I was nervous about. Five stations stand between me and the mempool. Each one can throw me out, and none of them care who my owner is.

Mempool acceptance combines consensus rules (which every block must obey) with policy rules (which this node applies to unconfirmed transactions). The order shown here is simplified; the checks are real.

In BCHN these live mainly in CheckTransaction, AcceptToMemoryPoolWorker, and CheckInputScripts.

Stage 3 · Check 1 of 5

Am I well-formed?

The first station just looks at my shape. Am I a sensible size? Do I have at least one input and one output? Do my numbers add up to something possible? I do.

Structural checks: the transaction parses, has inputs and outputs, is under the size limit and at or above the minimum size, no output is negative or exceeds 21 million BCH, and no two inputs reference the same outpoint.

Highlighted: the framing fields the node reads first: version, the two counts, and locktime.

Stage 3 · Check 2 of 5

Do my coins exist?

The second station opens its ledger of every spendable coin on the network and looks up the two I claim to be spending. Both are there, both unspent. It also learns what they are worth, because I never said.

The node looks up each outpoint in its UTXO set (and the mempool, for unconfirmed parents). A missing coin means the transaction is orphaned or invalid. This lookup is also where the node learns the input amounts, which it needs for the fee check and for the signature digest.

The UTXO set is keyed by (txid, index). Each entry stores the amount, the locking script, the height it was created at, and whether it was a coinbase.

Stage 3 · Check 3 of 5

The big one: does my key fit the lock?

Each coin I carry was locked with a tiny program by whoever gave it to my owner. To spend the coin, I must run that program and satisfy it. The node runs it step by step, on a little machine with a stack. Watch the stack. Every value on it is real.

For every input, the node runs the unlocking script and then the coin's locking script in the Bitcoin Cash script VM, sharing one stack. The trace on the stage is a real evaluation of input 0 by Libauth's implementation of the same VM.

Script is a stack language. Pushes put bytes on the stack; operators pop and push. There are no variables, only the stack.

Stage 3 · Check 4 of 5

Did I pay my way?

The fourth station weighs me. I am 360 bytes and I left 400 sats unclaimed. That is a hair over one satoshi per byte, which is the going rate on Bitcoin Cash, and has been for years. Pocket change, but enough to make spam expensive.

Fee = inputs − outputs. It is never written down; the node computes it from the amounts it looked up in check 2. Policy requires at least the minimum relay fee rate, and mining pools generally accept anything at or above it because blocks have plenty of room.

Rate is fee divided by serialized size in bytes. BCH does not use weight units or a witness discount; a byte is a byte.

Stage 3 · Check 5 of 5

Has anyone spent these coins already?

The last station keeps a list of every coin already claimed by a transaction waiting in the mempool. Mine are not on it. And look who is behind me: a copy of me, spending the same coin to a different address. It arrived second, so it loses. First seen wins. No do-overs.

A conflicting transaction that spends an outpoint already spent by a mempool transaction is rejected. Bitcoin Cash does not implement replace-by-fee, so the first valid spend the node sees is the one it keeps. On detecting the conflict, the node broadcasts a double-spend proof (DSProof) so merchants learn within seconds that someone attempted it.

A DSProof contains the two conflicting inputs' signatures and the signing serialization components needed to verify both. It proves that a key signed two different spends of one outpoint, without revealing anything else.

Stage 3 · Inside a full node

Five for five. Welcome to the mempool.

I made it. I am valid, funded, signed, paid for, and first. The gate opens onto a big pool where every accepted transaction floats until a block comes along. This node has already told its fourteen peers about me. Every one of them is running the same five checks right now.

The transaction is added to this node's mempool and announced to peers. From here on it is a candidate for the next block. There is no fee auction on Bitcoin Cash in normal conditions: blocks are large enough that everything paying the minimum rate gets in.

Mempool entry records the transaction, its fee, its size, its arrival time, and its ancestors and descendants for package handling.

Stage 4 · The mempool

The pool.Every valid transaction floats here until a block comes.

It is literally a pool. Everyone who passed the five checks is in here, bobbing, waiting. Nobody is in a hurry, because there is nothing to hurry for. A block will come when it comes.

The mempool is this node's private set of accepted, unconfirmed transactions. Every node keeps its own; they mostly agree but never perfectly. It is bounded in size and transactions expire from it after two weeks if never mined, though on Bitcoin Cash that essentially never happens.

Mempool entries are indexed several ways at once: by txid, by fee rate, by arrival time, and by ancestry, so that block assembly and eviction are cheap.

Stage 4 · The mempool

House rules: no auction, no VIP section.

There is a sign on the wall. First seen, first served. No replacing yourself with a pricier version. And there is room for everyone, because blocks here are big. Everyone pays about one satoshi per byte, and that is all anyone pays.

Bitcoin Cash blocks are at least 32 MB and the limit now adjusts upward with sustained demand. Typical blocks are far from full, so there is no bidding war for space and the fee rate has stayed near the one satoshi per byte floor for years. Without replace-by-fee, the first valid spend of a coin is the one that stays.

block limit 32 MB floor, adaptive (ABLA) · typical fee ~1 sat/B · RBF none

Since May 2024 the block size limit is set by the Adaptive Blocksize Limit Algorithm: it grows in response to sustained block usage and never drops below 32 MB.

Stage 4 · The mempool

How long do we float? Nobody knows.

Blocks are a lottery, not a schedule. Miners hash away, and every ten minutes on average one of them gets lucky. On average. The clock on the wall does not help: a block is just as likely in the next minute whether we have waited thirty seconds or thirty minutes.

Block discovery is a memoryless process. The difficulty algorithm (ASERT) steers the long-run average to ten minutes, but individual gaps vary wildly: a couple of seconds, or over an hour. Merchants accept zero-confirmation payments for everyday amounts precisely because the mempool stage is safe enough and the wait is unpredictable.

P(block in next minute) ≈ 10% · median gap ≈ 7 min · mean gap 10 min · P(> 30 min) ≈ 5%

Inter-block times are approximately exponential with rate 1/600 s. The chance of no block within t seconds is e^(−t/600).

Stage 4 · The mempool

A block! The pool drains.

Somewhere in the world a miner just found a valid block, and it includes all of us. Down the drain we go, every single one. On Bitcoin Cash there was room for everyone, so nobody is left floating.

A new block arrives over the same gossip network the transaction used. Our node removes every transaction the block contains from its mempool, and anything that conflicts with them. In the next room it verifies the block itself before trusting any of that.

Blocks propagate with compact block relay: peers send the header plus short transaction ids, and a node that already holds the transactions in its mempool reconstructs the block without downloading them again. Our transaction helped make the block cheap to relay.

Stage 5 · The block

The block chamber.A miner found it. Our node still checks everything.

The block arrives with an 80-byte header on top: a fingerprint of the previous block, a fingerprint of all of us, a timestamp, and the winning lottery number. Our node checks the miner's work before it believes a word of it.

The header commits to the previous block hash, the merkle root of every txid in the block, the time, the difficulty target, and the nonce. The node verifies that the header hash is below the target (proof of work), then re-validates every transaction it has not already validated in its mempool. Miners get no special trust.

header version · prev hash · merkle root · time · bits · nonce = 80 bytes
expected work ~2^71 hashes per block at current difficulty

Header layout: 4-byte version, 32-byte previous block hash, 32-byte merkle root, 4-byte time, 4-byte nBits, 4-byte nonce. SHA-256 twice over those 80 bytes must be numerically below the target encoded by nBits.

Stage 5 · The block

Everyone line up. Alphabetically.

Inside a block we stand in a fixed order: sorted by our names, the txids. Not by fee, not by who arrived first, not by who the miner likes. My name starts with a4, so I am somewhere in the middle. Nobody can buy a better spot.

Canonical Transaction Ordering (CTOR, since November 2018) requires the transactions after the coinbase to be sorted by txid. Any node can then reconstruct the block's order from the set of txids alone, which makes block relay and parallel validation far cheaper.

Txids are compared as 256-bit numbers in their displayed (big-endian) form. Parents may appear after children under CTOR; validation handles that with an outputs-first pass.

Stage 5 · The block

First in line: the coinbase.

One transaction stands at the front, wearing a crown. It has no inputs. It is how the miner gets paid: brand-new coins, plus every fee in the block, mine included. Those 400 satoshis I left behind? They end up here.

The coinbase is the block's first transaction and the only one allowed to create value. Its outputs may claim the block subsidy plus the sum of all fees. The subsidy halves every 210,000 blocks; after the April 2024 halving it is 3.125 BCH. Coinbase outputs cannot be spent for 100 blocks.

The coinbase input references the null outpoint (32 zero bytes, index 0xffffffff). Its unlocking script must begin with the block height (BIP34) and may carry up to 100 bytes of arbitrary data.

Stage 5 · The block

Into the block. One confirmation.

In we go, all of us, into the crystal. The block's fingerprint now depends on every one of us, and the next block will depend on this one. Change a single byte of me and the fingerprints stop matching. I have one confirmation.

The merkle root folds every txid pairwise into one 32-byte hash that sits in the header. The header hash is what the miner worked on, so the proof of work covers this transaction indirectly. Our node connects the block to its chain, updates its UTXO set, and relays the block to its peers.

UTXO set update: the inputs are deleted, the outputs are inserted as spendable coins tagged with the block height. A merkle proof of this txid needs only about log2(n) hashes.

Stage 6 · The archive

The chain.Kept by every node. There is no undo.

This is where I live now: block 966,240, one link in a chain that goes back to January 2009. Every full node on the network has a copy of me. There is no button anyone can press to take me back.

Each block header references the previous block's hash, so the chain is a single linked structure anchored at the genesis block. Rewriting a block means redoing its proof of work and that of every block after it, against the whole network's ongoing hashrate.

Blocks are stored on disk in blk*.dat files with an index in LevelDB. The chain state (the UTXO set) is stored separately and is what validation actually reads.

Stage 6 · The archive

Another block on top. Two confirmations.

Ten-ish minutes later, another block lands on top of mine. Now anyone who wanted to undo me would have to redo two blocks' worth of work, faster than the entire network adds new ones. It only gets harder from here.

Confirmations count the block containing the transaction plus every block built on it. Reorganizations of one or two blocks happen occasionally by accident when two miners find blocks at nearly the same time; the transaction is almost always in both candidates and simply reappears.

An attacker with fraction q of the hashrate who is z blocks behind catches up with probability roughly (q/p)^z. At q = 0.1 and z = 2 that is about 1%; at z = 6 it is about 0.02%.

Stage 6 · The archive

Six deep. Settled.

Six blocks on top is the old rule of thumb for "this is final". On Bitcoin Cash there is a harder line too: once I am ten blocks deep, nodes running BCHN refuse to reorganize me away at all. I am part of history now.

Six confirmations is convention, not consensus; exchanges and merchants choose their own thresholds by amount. BCHN additionally finalizes blocks at a depth of ten: it will not accept a competing chain that would reorganize a block more than ten deep, which caps how far back any reorg can reach.

Finalization is a node policy (rolling checkpoints), enforced identically by every BCHN node, so the network converges on it without it being part of the block validity rules.

Stage 6 · The archive

My work is done. My outputs are just getting started.

Remember the blueprints from the wallet? They are real coins now. 42,000 sats sit on the merchant's shelf. 5,100 sats of change went back to my owner's shelf. Some day a wallet will pour one of them into a brand-new transaction, and the whole ride starts again. Me? I am a record. I rest.

A transaction's outputs become UTXOs the moment the block connects. They are identified by this txid and an index: a44d3a68…:0, …:1 and so on. Any future transaction spending them will reference exactly those outpoints, the way this one referenced its own inputs.

This is the whole model: coins are never balances, only outputs. The global state is the set of unspent outputs, and every transaction is a function from some of them to some new ones.

The end

That was one transaction's whole life.

  1. The wallet: born, filled, written, signed, named
  2. The network: broadcast, gossip, everywhere, arrival
  3. Validation: shape, coins, script, fee, first-seen
  4. The mempool: the pool, the rules, the lottery, the drain
  5. The block: proof of work, canonical order, coinbase, sealed in
  6. The archive: confirmations, finality, and the outputs become coins

Now paste a txid at the top and watch your own transaction take the same ride.

↺ Ride again

everything on this page was computed, not typed