A blockchain is a git repo with a hard problem
My notes from a university blockchain course still say the block reward is 6.25 BTC. That's a halving ago; the network moved on in April 2024 and the notes didn't. Numbers about Bitcoin rot fast. The data structure underneath doesn't, because it can't: every one of the ~959,000 blocks mined since 2009 is welded to the previous one by its hash, and changing anything old means redoing everything since. This series is about that structure, and it starts where my notes aged best: the bytes.
(If you'd rather start further back, none of this was invented in 2008 — the chain of blocks is from 1991, the proof-of-work from 1997, and that prologue runs both. This post assumes none of it.)
If you read my git series, you already own the key idea. Git names every object by the hash of its content, links commits by storing the parent's name, and gets tamper-evidence for free: touch anything in history and every hash above it changes. A blockchain is the same machine with one extra constraint bolted on, a rule that makes appending expensive and rewriting economically absurd. This first post ignores the constraint almost entirely (that's part three) and just reads the structure, the way we once read .git.
Everything below runs on real data. I fetched the raw bytes of four blocks from blockstream.info: the genesis block, the two blocks that first chained onto it, and one block from 2010 chosen for a reason you'll see in section 04. Behind the figures is a small chain engine in TypeScript with its own hand-rolled SHA-256, the same way the git posts carry their own SHA-1. Every hash on this page is computed in your browser from those bytes, and any block explorer will agree with every digit.
01 · A commit with no force-push
Put the two objects side by side and the family resemblance does most of the work. A git commit names a snapshot (its tree) and its history (its parent). A Bitcoin block header names its content (a merkle_root, the fingerprint of this block's transactions) and its history (prev_block, the hash of the previous header). Both are tiny. Both are named by the hash of their own bytes. Hover either column and watch the fields pair up:
Two rows on the right have no partner, and they're the whole difference between the systems. bits and nonce exist because git and Bitcoin answer opposite questions. Git asks: how do I detect that history changed? Detection is enough, because a repo has an owner; if someone force-pushes rewritten history, you notice, you complain, someone gets fired. Bitcoin asks: how do I make history that nobody owns stay put? There's no one to complain to, so detection isn't enough. Rewriting has to be more than visible. It has to be expensive. Those two fields are where the expense plugs in, and they get their own post.
Everything else about the resemblance is real, though, down to the terminology: both structures are hash-linked lists of headers, and both inherit the property that one object's name commits to the entire history behind it. A blockchain is a git repo where push --force costs more than electricity you can afford.
02 · 80 bytes, the whole header
Here is the actual genesis block header, all eighty bytes of it, exactly as it sits on every full node on Earth. Six fields. Hover each one:
Things worth noticing while you're in there:
prev_blockis 32 bytes of zero. There is no block before this one. Same move as git's first commit having noparentline, except Bitcoin can't omit the field, because the header is fixed-width, so it points at nothing instead. The genesis block is the one block that's hardcoded into the client rather than validated.- The
timestampdecodes to January 3rd, 2009. The next block's decodes to January 9th. Six days of silence between block 0 and block 1, and nobody knows for sure what Satoshi was doing in between. bitsis a 4-byte compressed number that unpacks into a 256-bit target. The rule of the whole game, one post early: a block only counts if the hash of its header is below this number. Look at the target's leading zeros, then look at the block's hash. That's not a coincidence; that's the rule holding.- The
versionsays 1, and this is where I have to defend my own figure. Modern headers almost never carry a tidy version number: since BIP9 the field is a bag of soft-fork signal bits, and miners even grind its spare bits as extra nonce space. In 2009 it just meant "one."
And one thing the figure makes honest that explorers hide: the famous hash 000000000019d668… is the SHA-256 (twice — Bitcoin double-hashes everything, a Satoshi-era habit) of these 80 bytes, byte-reversed. On the wire and on disk the hash ends in zeros; Satoshi's client printed it backwards, every explorer since has copied that, and everyone's first from-scratch implementation "produces wrong hashes" for exactly this reason. Mine did. Flip the toggle in the figure and watch the reversal happen; it's the single most useful thing to know before reading Bitcoin bytes yourself.
One more resident of these bytes deserves a mention. The genesis block's single transaction carries a text field, and the text is The Times 03/Jan/2009 Chancellor on brink of second bailout for banks, a newspaper headline from the morning the chain started: part timestamp proof, part mission statement, permanently welded in starting at byte 131 of the first block. We'll open that transaction properly in part two.
03 · Now break it
Tamper-evidence is the claim everyone repeats about blockchains. Here it is as a mechanism instead of a slogan. Below are the first three blocks that ever existed, each header re-hashed live, each prev_block checked against its neighbor. Then go ahead: bump block 1's nonce, the one field a miner is meant to vary, and watch what your edit of a single number does:
Two separate things break, and it's worth separating them:
- Block 2 still points at the old hash. Your edited block 1 has a new name, and nobody asked for block 2's opinion. The link goes red. This is exactly the git cascade from part one of that series: change a blob and every tree and commit above it needs rewriting. So far, nothing git couldn't do.
- Block 1's hash stopped starting with zeros. This is the new failure mode. Even if you were willing to rewrite block 2, and 3, and all ~959,000 that follow, every single rewritten header would need its hash back under the target, and the only known way to get it there is brute force over the nonce. The network currently performs on the order of 900 quintillion of those double-hashes per second and one block still takes ten minutes to find. Your laptop is not catching up.
That second failure is the entire innovation, and I'm deliberately leaving it as a cliffhanger; when we build an actual miner in your browser in part three, you'll feel the difficulty in your fan. For now the honest summary is: git detects rewrites, Bitcoin prices them.
04 · A tree on Pizza Day
One field of the header is still unexplained: merkle_root, the 32 bytes that stand in for "everything this block contains." The git series ended on Merkle trees: a tree object's hash commits to its children's hashes, recursively, so the root commits to everything. Bitcoin uses the same construction, flattened: no filenames, no nesting, just the list of this block's transaction IDs paired up and hashed, pairs of pairs hashed again, until one hash remains. (The structure itself gets a post of its own, including the wart below taken apart properly.)
The block below is real, and I picked it with intent. Block 57055 was mined on May 22nd, 2010, about an hour after the block that confirmed the famous 10,000 BTC pizza payment, and it carries exactly three transactions, because Bitcoin's Merkle tree has a wart I refuse to hide from you. Click any transaction to corrupt one byte of it and watch the damage climb:
Things worth watching for:
- The flip cascades. One byte in a transaction changes its txid, which changes one interior node, which changes the root, which no longer matches the
merkle_rootsitting in the block's own header. A transaction cannot be edited quietly. This is also why the root lives in the header: the 80 bytes commit to every transaction without containing any of them. - The third leaf pairs with itself. Three is odd, and Bitcoin's rule for odd levels is to duplicate the last hash. That's the wart. It's not elegant, it even enabled a real vulnerability (CVE-2012-2459: two different transaction lists can produce the same root; Bitcoin Core carries a workaround to this day), and a later proposal to fix the construction was never adopted. I implemented the duplication faithfully because without it, this block's real root doesn't reproduce. Warts are consensus too.
- The coinbase mints 50 BTC. First transaction of every block, its single input pointing at nothing, reward out of thin air. My course notes' 6.25 figure was three halvings after this block's 50, and we're one more along at 3.125. The structure holds still while the numbers drain.
Why bother with a tree instead of hashing the concatenated transactions? Because a tree lets you prove one transaction is in a block by showing only the hashes along its path — a dozen hashes instead of the whole block. That trick, and the kind of stripped-down node it enables, is part four.
05 · What's next
The mental model, compressed: a Bitcoin block is a commit object with a fixed-width header; prev_block is parent, merkle_root is tree, and the two fields with no git name, bits and nonce, are a padlock we haven't opened yet. The chain is a hash-linked list you can verify with a couple hundred lines of TypeScript and no trust in me: every hash in this post was computed in your browser from bytes any explorer will hand you.
Honesty about where the toy ends: my engine reads pre-2017 blocks only. Modern blocks carry segwit transactions, whose serialization (and a second, parallel Merkle tree for witnesses) my parser doesn't speak; blocks are capped by weight these days, not the old 1 MB, and a real one runs 1.5–2.5 MB with traffic my 2010 fixtures never dreamed of, inscriptions included. None of that changes an idea in this post; the 80-byte header hasn't moved since block 0.
The series continues the way the git one did, by following the pointers:
- Ownership: what's inside a transaction, why you don't have a balance, and a stack machine that decides who spends what
- Consensus: the hard problem, with a real miner in your browser
- Storage: what a full node keeps on disk, and how to verify a payment with twelve hashes
Same promise as always: it's bytes all the way down, and you can read every one of them.