The Blockchain Chief Bitcoin Book / Part I: Foundations
Chapter 02

Transactions & Blocks

The fundamental data structures that represent value transfer and history in Bitcoin: CTransaction, CTxIn, CTxOut, CBlock, and their relationships.

The Big Idea

Bitcoin has two core primitives:

Everything else in Bitcoin Core (validation, networking, wallets) exists to create, verify, relay, and store these two data structures. This chapter shows you exactly what they look like in C++.

๐Ÿ’ก UTXO Model

Bitcoin doesn't track "balances." Instead, it tracks unspent transaction outputs (UTXOs). To spend bitcoin, you consume one or more existing UTXOs and create new ones. A transaction is simply a list of inputs (UTXOs being spent) and outputs (new UTXOs being created).

COutPoint: Pointing to a UTXO

Every input needs to reference which previous output it's spending. A COutPoint does exactly that: it's a (txid, index) pair:

Txid hash 32-byte transaction hash
uint32_t n Output index in that transaction's vout (0-based)

The special value NULL_INDEX (0xFFFFFFFF) with a null hash marks a coinbase input, the first transaction in every block that creates new coins.

// Example: reference the 0th output of transaction abc123...
COutPoint prevout(Txid::FromHex("abc123..."), 0);

// Coinbase inputs have a null outpoint
assert(coinbase_tx.vin[0].prevout.IsNull()); // true

CTxIn: Transaction Inputs

A transaction input consumes an existing UTXO and provides proof (a signature) that the spender is authorized:

COutPoint prevout Which UTXO this input spends
CScript scriptSig Unlocking script (signature + pubkey for P2PKH)
uint32_t nSequence Sequence number (RBF, relative locktime)
CScriptWitness scriptWitness SegWit witness data (not in legacy serialization)

The Sequence Number

The nSequence field has evolved to serve multiple purposes:

CTxOut: Transaction Outputs

An output is where new UTXOs are born. It specifies an amount and a spending condition:

CAmount (int64_t) nValue Amount in satoshis (1 BTC = 100,000,000 sat)
CScript scriptPubKey Locking script, the spending condition

Output Types

The scriptPubKey determines what kind of output it is:

Interactive: Transaction Anatomy

Click any part of the transaction below to see what it contains and how it's serialized:

CTransaction: The Complete Transaction

CTransaction is the immutable representation of a Bitcoin transaction. Once constructed, its fields cannot be changed.

vector<CTxIn> vin Inputs (UTXOs being spent)
vector<CTxOut> vout Outputs (new UTXOs being created)
uint32_t version Transaction version (currently 2)
uint32_t nLockTime Earliest time/block the tx can be mined

Under the hood, CTransaction computes and caches two hashes:

โœ… Why Two Hashes?

Before SegWit, a third party could change the signature (witness) data without invalidating the transaction. This changed the txid and broke chains of unconfirmed transactions. SegWit fixed this by moving witness data outside the txid computation. The txid is now "malleability-free."

CMutableTransaction

Need to build or modify a transaction? Use CMutableTransaction. It has the same fields as CTransaction but they're mutable. The typical workflow:

// 1. Create a mutable transaction
CMutableTransaction mtx;
mtx.version = 2;

// 2. Add inputs
mtx.vin.push_back(CTxIn(COutPoint(prev_txid, 0)));

// 3. Add outputs
mtx.vout.push_back(CTxOut(amount, scriptPubKey));

// 4. Sign inputs (fills in scriptSig/scriptWitness)
SignTransaction(mtx, keystore, ...);

// 5. Convert to immutable CTransaction
CTransactionRef tx = MakeTransactionRef(std::move(mtx));

CBlockHeader: 80 Bytes of Truth

The block header is only 80 bytes, yet it secures the entire block:

int32_t nVersion Block version (signals soft fork support)
uint256 hashPrevBlock Hash of the previous block header
uint256 hashMerkleRoot Merkle root of all transactions in this block
uint32_t nTime Block timestamp (Unix epoch seconds)
uint32_t nBits Difficulty target in compact format
uint32_t nNonce Miner's nonce, varied to find valid PoW

The hashPrevBlock field is what creates the chain - each block points back to its parent, forming an unbroken chain all the way to the genesis block.

CBlock: Header + Transactions

CBlock inherits from CBlockHeader and adds the transaction list:

(inherited) CBlockHeader fields All 6 header fields above
vector<CTransactionRef> vtx All transactions (vtx[0] is always the coinbase)

The Merkle Root

The hashMerkleRoot in the header is computed by building a binary tree of transaction hashes:

  1. Hash each transaction (txid) to get leaf nodes.
  2. Pair adjacent hashes and hash them together.
  3. If there's an odd number, duplicate the last one.
  4. Repeat until one hash remains. That's the Merkle root.

This allows Merkle proofs, a compact proof that a transaction is included in a block, without downloading the entire block.

Serialization & SegWit

Transactions are serialized differently depending on whether they have witness data:

Legacy Format

[version][vin count][vin...][vout count][vout...][locktime]

SegWit Format (BIP 144)

[version][marker=0x00][flag=0x01][vin count][vin...][vout count][vout...][witness...][locktime]

The marker byte (0x00) signals that this is a SegWit transaction. The witness data follows the outputs and is not included in the txid hash (only in the wtxid).

๐Ÿ’ก Weight vs. Size

SegWit introduced a new metric: weight. Non-witness data counts as 4 weight units per byte; witness data counts as 1. The block weight limit is 4,000,000 weight units (equivalent to ~1 MB of non-witness data, or up to ~4 MB with witness data). This effectively gives a "discount" to witness data.

Coinbase Transactions

The first transaction in every block is the coinbase transaction. It's special:

The block subsidy started at 50 BTC and halves every 210,000 blocks (~4 years). After the 2024 halving, it's 3.125 BTC per block.

// Check if a transaction is a coinbase
bool CTransaction::IsCoinBase() const {
    return (vin.size() == 1 && vin[0].prevout.IsNull());
}