The Blockchain: Linking Blocks
You can now build a single Block and compute its hash. But one block is not a chain. In this chapter
we build the Blockchain type itself: the ordered list of blocks, the first block (the genesis
block), and — the whole point — the links that chain each block to the one before it.
That link is one field doing enormous work. Because every block’s header stores the hash of the previous block, the blocks form a one-way chain where changing any old block would break every link after it. This is what makes a blockchain tamper-evident, and it’s the answer to the book’s recurring question: how do untrusting strangers agree on one history? They agree because the history is welded together by hashes.
Open src/blockchain.rs.
Money constants and the subsidy
Section titled “Money constants and the subsidy”Before the chain itself, the file defines a few constants that set the economics of our coin:
/// The smallest unit; 1 coin = 100,000,000 units (like satoshis).pub const COIN: u64 = 100_000_000;/// Block reward before the first halving.pub const INITIAL_SUBSIDY: u64 = 50 * COIN;/// How many blocks between halvings.pub const HALVING_INTERVAL: u64 = 210_000;/// Required leading zero bits for a valid block hash (low, for fast demos).pub const DIFFICULTY_BITS: u32 = 16;A const is a compile-time constant — a name for a fixed value. We work in the smallest unit, like
Bitcoin’s satoshis: one COIN is 100 million units, so we never need fractions. INITIAL_SUBSIDY is the
reward a miner gets for a fresh block — 50 coins, just like early Bitcoin. HALVING_INTERVAL is how
often that reward is cut in half. DIFFICULTY_BITS we’ll use for mining; it’s set low here so demos
finish fast.
Now the subsidy function — how many new coins a block at a given height creates:
/// The block subsidy at a given height (halves every `HALVING_INTERVAL`).pub fn subsidy(height: u64) -> u64 { let halvings = height / HALVING_INTERVAL; if halvings >= 64 { 0 } else { INITIAL_SUBSIDY >> halvings }}Read it top to bottom. We figure out how many halvings have happened by integer-dividing the block
height by the interval. Then the clever part: INITIAL_SUBSIDY >> halvings. The >> operator is a
bit shift right, and shifting an integer right by one bit is the same as dividing by two. So shifting
by halvings divides the reward by two, that many times — exactly what “halving” means. The guard if halvings >= 64 returns 0 first, because shifting a 64-bit number by 64 or more is undefined; after
enough halvings the reward is effectively zero anyway, so the coin supply caps out.
The Blockchain struct
Section titled “The Blockchain struct”Here’s the type at the center of everything:
pub struct Blockchain { pub blocks: Vec<Block>, pub utxos: UtxoSet,}Just two fields. blocks is a Vec<Block> — a growable, ordered list of blocks. Order is history:
blocks[0] is the genesis block, blocks[1] extends it, and so on. The utxos field holds the
UTXO set: the live ledger of every coin that currently exists and who can spend it. (We build the
UTXO model in its own chapter — for now, think of it as “the current balances.”) Every method we write
either reads from these two fields or carefully updates them.
Creating the chain: the genesis block
Section titled “Creating the chain: the genesis block”Every chain needs a first block. It can’t point at a previous block (there isn’t one), so it’s special
and we build it by hand in new:
impl Blockchain { /// Create a new chain with a mined genesis block (coinbase to the /// unspendable zero address, like Bitcoin's genesis). pub fn new() -> Self { let mut chain = Blockchain { blocks: Vec::new(), utxos: UtxoSet::new(), }; let coinbase = create_coinbase(ZERO_HASH, 0, 0, 0); let txs = vec![coinbase]; let root = merkle_root(&txs.iter().map(|t| t.txid()).collect::<Vec<_>>()); let mut genesis = Block { header: BlockHeader { version: 1, prev_block_hash: ZERO_HASH, merkle_root: root, timestamp: 0, bits: DIFFICULTY_BITS, nonce: 0, }, transactions: txs, }; genesis.mine(); for tx in &genesis.transactions { chain.utxos.add_transaction(tx.txid(), tx); } chain.blocks.push(genesis); chain }new returns Self — shorthand for “the type this impl block is for,” i.e. Blockchain. Step by
step:
- We start with an empty chain:
Vec::new()is an empty vector,UtxoSet::new()an empty ledger. It’slet mut chainbecause we’re about to modify it. create_coinbase(ZERO_HASH, 0, 0, 0)builds the coinbase transaction — the special one that mints new coins. The genesis coinbase pays theZERO_HASHaddress, which nobody holds the key to, so (like Bitcoin’s genesis) those first coins are unspendable. We wrap it in a one-element vector withvec![coinbase].merkle_root(...)folds the transaction ids into the header’smerkle_root. We’ll explain that pipeline in the Merkle chapter; for now note the genesis still commits to its transactions.- We construct the
genesisblock. The crucial field isprev_block_hash: ZERO_HASH— the genesis has no parent, so it points at all-zeros. Every later block will point at a real hash. genesis.mine()runs the Proof of Work loop to find a valid nonce (covered in the mining chapter).- The
for tx in &genesis.transactionsloop records the new coins in the UTXO set, thenchain.blocks.push(genesis)appends the block. The barechainon the last line returns the finished chain.
Rebuilding a chain from stored blocks
Section titled “Rebuilding a chain from stored blocks”When we later save the chain to disk and reload it, we get back a Vec<Block> but no UTXO set. The UTXO
set is derived state, so we replay the blocks to rebuild it:
/// Rebuild a chain from stored blocks, replaying them to derive the UTXO set. pub fn from_blocks(blocks: Vec<Block>) -> Self { let mut utxos = UtxoSet::new(); for block in &blocks { for tx in &block.transactions { utxos.add_transaction(tx.txid(), tx); } } Blockchain { blocks, utxos } }Two nested for loops walk every transaction in every block and feed it to the UTXO set. By the end,
utxos reflects exactly the balances those blocks imply. This is a recurring blockchain idea: the chain
of blocks is the source of truth, and everything else can be recomputed from it.
Asking the chain about itself: tip and height
Section titled “Asking the chain about itself: tip and height”Two small read-only helpers tell us where the chain currently ends:
pub fn tip_hash(&self) -> Hash { self.blocks.last().map(|b| b.hash()).unwrap_or(ZERO_HASH) }
/// Height of the tip (genesis is height 0). pub fn height(&self) -> u64 { (self.blocks.len() as u64).saturating_sub(1) }tip_hash gives the hash of the most recent block — the tip — which is exactly what the next
block must store in its prev_block_hash. The implementation is a nice tour of Option, Rust’s way of
expressing “maybe there’s a value here.” self.blocks.last() returns an Option<&Block>: Some(block)
if the chain is non-empty, or None if it’s empty. We .map(|b| b.hash()) to turn a present block into
its hash, then .unwrap_or(ZERO_HASH) to supply a fallback when there’s nothing there. So an empty chain
reports ZERO_HASH — meaning a brand-new block would correctly “extend” the void, the same sentinel the
genesis used.
height returns the position of the tip, counting genesis as 0. We take the number of blocks, cast it to
u64 with as, and subtract one — but with saturating_sub(1), which clamps at 0 instead of
underflowing if the chain were somehow empty. (Subtracting 1 from an unsigned 0 would otherwise be a
bug.)
Adding a block: the link in action
Section titled “Adding a block: the link in action”Finally, add_block is how the chain grows:
/// Validate then append a block, updating the UTXO set. pub fn add_block(&mut self, block: Block) -> Result<(), String> { self.validate_block(&block)?; for tx in &block.transactions { self.utxos.add_transaction(tx.txid(), tx); } self.blocks.push(block); Ok(()) }}At a high level, add_block does three things, in order: it validates the block, updates the UTXO
set with the block’s transactions, and appends the block to the list. It takes &mut self because
it changes the chain, and returns Result<(), String> — either Ok(()) for success, or Err with a
message if the block is rejected. The ? after validate_block is shorthand for “if this returned an
error, stop and return that error now”; we’ll lean on ? a lot once we reach error handling.
The first and most important check inside validate_block is the link itself: a new block is only
accepted if its prev_block_hash equals the current tip_hash(). That single comparison is what keeps
the chain a single, ordered history rather than a loose bag of blocks.
Why the link makes tampering obvious
Section titled “Why the link makes tampering obvious”Here’s the payoff. Each block’s hash is computed from its header, and the header includes
prev_block_hash. So block 5’s hash depends on block 4’s hash, which depends on block 3’s, all the way
back to genesis. If an attacker quietly edits a transaction in block 2, block 2’s Merkle root changes,
so block 2’s hash changes — but block 3 still stores the old block 2 hash in its prev_block_hash. The
link no longer matches. To hide the tampering they’d have to re-mine block 2 and block 3 and every
block after it, faster than the honest network extends the chain. That’s the wall Proof of Work builds,
and the chain link is the mortar holding it together.
One last detail: Default
Section titled “One last detail: Default”The file ends with a small convenience:
impl Default for Blockchain { fn default() -> Self { Self::new() }}Default is a standard trait for “the obvious starting value” of a type. By implementing it, anyone can
write Blockchain::default() (or use it in contexts that call default() automatically) and get a fresh
chain with its genesis block. Here it just delegates to new(), so the two are interchangeable.
Try it
Section titled “Try it”The companion crate’s demo builds a chain and adds blocks. Run it and watch the links:
cargo run -- demoLook closely at the printed block hashes. The genesis hash becomes block 1’s prev_block_hash; block
1’s hash becomes block 2’s prev_block_hash. Each block reaches back and grips the one before it — that
chain of hashes is the blockchain.
Check your understanding
Section titled “Check your understanding”- What two fields does the
Blockchainstruct hold, and why can one of them always be recomputed from the other? - In the genesis block, why is
prev_block_hashset toZERO_HASH? - How does
subsidy()use the>>operator to implement halvings, and why is a bit shift used instead of floating-point division? tip_hash()returnsZERO_HASHwhen the chain is empty. Trace howOption,.map, and.unwrap_orproduce that result.- If an attacker edits a transaction in an old block, why does every block after it stop validating?
Show answers
blocks(the orderedVec<Block>, the canonical history) andutxos(the UTXO set, the live ledger). The UTXO set can always be recomputed from the blocks by replaying every transaction from genesis — it’s derived state, a cache over the source of truth.- Because the genesis block has no parent — there’s no previous block to point at — so its
prev_block_hashuses the all-zerosZERO_HASHsentinel meaning “nothing before this.” Every later block points at a real previous hash. subsidy()computeshalvings = height / HALVING_INTERVAL, then returnsINITIAL_SUBSIDY >> halvings— and shifting right by one bit divides by two, so shifting byhalvingshalves the reward that many times. A bit shift is used instead of floating-point division because it’s a single, exact, integer-only operation with no rounding surprises, which matters in code that decides who owns money.self.blocks.last()returnsOption<&Block>—Some(block)if non-empty, elseNone..map(|b| b.hash())turns a present block into its hash (and leavesNoneasNone);.unwrap_or(ZERO_HASH)then suppliesZERO_HASHas the fallback when there’s nothing there — so an empty chain reportsZERO_HASH, the same sentinel a brand-new genesis “extends.”- Editing a transaction changes that block’s Merkle root, hence its header hash — but the next block still stores the old hash in its
prev_block_hash, so the link no longer matches and it fails validation. To hide it the attacker would have to re-mine that block and every block after it, faster than the honest network extends the chain — which is the wall Proof of Work builds.