Skip to content

Revision — The Clock

This part answered the first sub-question hiding inside the book’s throughline. The throughline asks how do you build a single global state machine that runs at hardware speed without falling apart? — and a state machine is nothing but a thing that applies an ordered list of inputs, one after another. So before “run it fast” comes a smaller, meaner question: in what order did the inputs arrive? On one computer that answer is free. Across thousands of machines that distrust each other, it is the most expensive thing in the whole system. Proof of History is Solana’s answer, and this page retells the whole arc in one pass so the pieces lock together.

No new mechanics here, and no questions to check yourself against. Just the story, told straight through — from why ordering is expensive to why ordering first buys you throughput — with the code you already read pinned at each step.

The problem: ordering is the expensive part

Section titled “The problem: ordering is the expensive part”

We started at Why Ordering Is Expensive with an uncomfortable fact: there is no trustworthy global clock across distrusting machines. Every validator has its own wall clock, its own network latency, its own view of “when did this transaction show up.” A timestamp is just a claim, and a claim from a machine you don’t trust is worth nothing. So classic distributed systems don’t measure time — they negotiate it. Validators exchange messages — “when did you see this? here’s what I saw” — and run rounds of voting until they agree on a sequence. That agreement is order, and it is bought with conversation.

At a few hundred transactions per second, the chatter is tolerable. At the tens of thousands per second Solana targets, it is the bottleneck that eats the machine. Every message is a round-trip; every round-trip is milliseconds; every millisecond spent agreeing on sequence is a millisecond not spent executing. The insight that opens the whole part is that ordering and agreement are two separate jobs bundled together by habit, and if you could establish order without conversation, you would delete the bottleneck outright.

the classic way: order IS the conversation
──────────────────────────────────────────
V1 ──"I saw tx at t=5"──► V2
V2 ──"I saw it at t=7"──► V3 rounds of messaging
V3 ──"let's agree t=6"──► V1 just to fix a sequence
▲ → the throughput wall
the PoH way: order is a fact in the data
──────────────────────────────────────────
read the chain → the order is already there → no round-trips

The fix, built in A Clock Made of Hashes, is almost too simple to believe. Take SHA-256 and feed its output back into itself, forever:

// from rust/solmini/src/poh.rs
pub fn hash_once(h: &Hash) -> Hash {
sha256(h) // one bare tick: h = sha256(h)
}

Because SHA-256 is pre-image resistant, the only way to know the value after N iterations is to actually compute all N of them, one after another. You cannot skip ahead to iteration one million without doing the 999,999 before it, and you cannot split the work across cores, because each hash needs the previous hash as its input. Production is strictly, unavoidably serial.

That single constraint is what turns a hash function into a clock. If nobody can fast-forward the chain, then the length of the chain — the count of hashes performed — is honest proof that real, sequential work happened, which means real time passed. A hash count is elapsed work, and elapsed work is a clock that cannot be forged. No wall clock, no trusted timestamp, no vote: just a number that could only have been produced by grinding through every step.

A clock that only counts is a metronome, not a ledger. The step that makes it useful came in Stamping Events Into Time: to place an event in the timeline, you mix it in. Instead of hashing the state alone, you hash the state together with the event:

// from rust/solmini/src/poh.rs
pub fn hash_with(h: &Hash, mixin: &Hash) -> Hash {
let mut hasher = Sha256::new();
hasher.update(h); // the rolling state
hasher.update(mixin); // sha256(the event)
hasher.finalize().into()
}

From that hash onward, every future value of the chain depends on that event. That is what “sealed in time” means, and it is the quiet genius of the whole design: order is established by construction. The event provably happened after everything hashed before it and before everything hashed after it — not because anyone said so, but because the arithmetic of the chain wouldn’t exist otherwise. You cannot move the event earlier or later without redoing every hash that follows. The reordering_two_events_breaks_the_chain test in poh.rs proves exactly this: swap two recorded events and the chain simply fails to verify, because their positions were baked into the hashes.

genesis ─►(tick)─►(tick)─►(record A)─►(tick)─►(record B)─►(tick)─►
▲ ▲
A sealed here B sealed here, after A
every later hash depends on A, then on B

Calibration: ticks, slots, and hashes-per-tick

Section titled “Calibration: ticks, slots, and hashes-per-tick”

Raw hash counts are an ordinal — they tell you the order and the relative distance between events, but “190 hashes since genesis” is not yet a duration or a schedule. Ticks, Slots, and Hashes-per-Tick added the calibration layer that turns the raw count into a real-time cadence.

A tick is the runtime hashing a fixed number of times and emitting an entry that says “time passed and nothing happened” — the heartbeat that keeps the chain moving even when no transactions arrive. Hashes-per-tick is the tuning knob: fix how many hashes make up one tick, and on known hardware the tick stream tracks wall-clock time. Group a fixed number of ticks and you get a slot — the unit that matters operationally, because each slot has one assigned leader.

hashes ──► ticks ──► slots
(raw count) (steady (one leader
cadence) per slot)
count ⇒ order (always) , count ⇒ time (once the machine is fixed)

That last chain of implications is the whole point of calibration. The count always gives you order. Once you fix hashes-per-tick on real hardware, the count also gives you time — and a predictable slot boundary gives you a known leader schedule. Everyone can compute, in advance, who produces the chain during which slot, with no election per slot. Treat the exact figures as version-dependent (slot targets around 400 ms have been used, as of 2024), but the durable fact is the shape: hashes to ticks to slots, count to order to time.

Verification: produce serial, verify parallel

Section titled “Verification: produce serial, verify parallel”

Here is the asymmetry that makes the whole thing pay off, from Verifying the Clock in Parallel. Producing the chain is serial and slow, on purpose — that is what makes its length honest. But verifying it is embarrassingly parallel. The reason is the checkpoint: every Entry records its own ending hash.

// from rust/solmini/src/poh.rs
pub struct Entry {
pub num_hashes: u64, // hashes since the previous entry
pub hash: Hash, // the chain state right after this entry
pub mixin: Option<Hash>, // Some(_) if the last hash folded an event in
}

Because each entry pins the exact hash the segment ends on, that hash is also the exact hash the next segment begins on. So a verifier already knows the start and the claimed end of every segment, and can hand each segment to a different core and check them all at once. The verifier does the same total number of hashes the producer did — there is no shortcut in the raw work — but it spreads that work across every core it has, while the producer was forced to use one. Slow to build, fast to check.

Out of that asymmetry fall the three trust properties the whole part was building toward, each one a passing test in poh.rs:

  • Tamper-evident — flip one byte of any recorded hash and verification fails at that entry (tampering_with_a_hash_is_caught). You cannot edit history without the recomputation exposing you.
  • Reorder-proof — an event’s position is part of what gets hashed, so swapping two events cannot verify (reordering_two_events_breaks_the_chain). Order is not a claim; it is a consequence.
  • No skip-ahead — you cannot fabricate a far-future state without doing the hashes in between; supply a hash you didn’t derive and it fails (a_forged_skip_ahead_fails).

One honest caveat we kept in view the whole time: PoH is a VDF-like construction, not a formally proven Verifiable Delay Function. Its “you had to spend real time” guarantee rests on SHA-256 being genuinely sequential to compute — nobody having a magic way to parallelize a single hash chain — rather than on a hardness proof. It is a strong, well-understood assumption, but it is an assumption, and worth naming rather than hiding.

Everything converges on the claim from Order First, Vote Second, and it is the reason this part is the first pillar of the book. PoH establishes order before consensus ever runs. Because the sequence is already a verifiable fact readable from the data, consensus no longer has to negotiate when things happened — it only has to vote on validity. That deletes the round-trips that used to sit on the critical path, and a machine with fewer round-trips on its critical path can run closer to hardware speed. Ordering-before-consensus is the throughput unlock; the rest of Solana is what you get to build once that unlock is in hand.

Which brings back the slogan from the overview, now earned rather than asserted:

Proof of History orders events. Consensus agrees on them. Those are two different jobs, and PoH does the cheap one first.

Keep the two jobs firmly separate in your head, because the next parts depend on the split:

  • PoH is the clock. It orders events and proves time passed. That is all it does — it chooses no winner, resists no Sybil, and picks no fork.
  • PoS / Tower BFT is the agreement. Stake decides who counts as a validator (Sybil resistance), and voting on the already-ordered history decides what is final. This is where “which fork wins” gets answered.

Conflate them and PoH looks like magic consensus that can’t possibly be safe. Separate them and it is exactly what it claims to be: a cheap, verifiable ordering primitive that hands a pre-sorted stream to a consensus layer, so the expensive layer does less work and the pipeline stays fed.

The single idea under this entire part is sequential work versus parallelizable work. PoH makes production sequential so the chain’s length is honest proof that time passed, and makes verification parallel so the whole network can check it cheaply. Hold that asymmetry, because Solana flips it on purpose later: when execution finally runs, Sealevel wants transactions that don’t depend on each other so they can run across every core at once. PoH makes production serial so time is trustworthy; execution goes parallel so throughput is high. Same distinction, aimed in opposite directions — that is the shape of the whole machine, and the clock is the piece that starts it running.