A Clock Made of Hashes
The previous page left us with an expensive bill: in a distributed system there is no clock anyone can trust, so validators normally have to talk — exchange messages until they agree on the order events happened. At thousands of transactions per second, that conversation is the bottleneck. The whole book asks one question — how do you build a single global state machine that runs at hardware speed without falling apart? — and ordering-by-conversation is exactly the kind of thing that makes it fall apart under load.
This page builds Solana’s answer to that bill. It is a clock that needs no conversation, because anyone can verify it from the data alone. The trick is small, strange, and powerful: take a hash function and feed its output back into itself, over and over. The number of times you had to do that becomes a measurement of real, elapsed work — and therefore of time.
The construction: a hash that eats itself
Section titled “The construction: a hash that eats itself”Take SHA-256. Start from a fixed seed and feed each output back in as the next input:
h0 = sha256(seed) h1 = sha256(h0) h2 = sha256(h1) h3 = sha256(h2) … hN = sha256(h(N-1))That is the entire idea. There is no cleverness hiding in the diagram — it is literally sha256 applied to its own previous result, N times. In the book’s companion crate solmini, this is two functions in poh.rs:
pub type Hash = [u8; 32];
/// Hash a byte slice once.pub fn sha256(input: &[u8]) -> Hash { let mut h = Sha256::new(); h.update(input); h.finalize().into() // GenericArray<u8, 32> -> [u8; 32]}
/// The starting state of a chain grown from `seed`.pub fn genesis(seed: &[u8]) -> Hash { sha256(seed)}
/// One step of the bare clock: `h = sha256(h)`.pub fn hash_once(h: &Hash) -> Hash { sha256(h)}genesis(seed) is h0. Every subsequent state is hash_once applied to the one before it. A generator that owns the rolling state and counts its own steps is all you need to run the clock:
pub struct Poh { state: Hash, count: u64 }
impl Poh { pub fn new(seed: &[u8]) -> Self { Poh { state: genesis(seed), count: 0 } }
/// Advance the bare clock by `n` hashes. pub fn tick(&mut self, n: u64) -> Entry { for _ in 0..n { self.state = hash_once(&self.state); self.count += 1; } Entry { num_hashes: n, hash: self.state, mixin: None } }}The count is the interesting field. It is not decoration — as the rest of this page argues, that single u64 is the clock reading. Stamping actual events into this stream (the mixin field, the record method) is the subject of the next page; here we care only about why the bare count is trustworthy in the first place.
Why there is no shortcut
Section titled “Why there is no shortcut”Ask the sharp question: given only the seed, how do you learn the value of hN for some large N — say a billion?
SHA-256 is pre-image resistant: given an output, there is no known way to recover an input that produces it that is faster than brute-forcing all possible inputs. So you cannot run the chain backwards, and — this is the load-bearing part — you cannot jump forwards either. There is no algebraic identity, no closed-form f(seed, N) that spits out hN without passing through h1, h2, … , h(N-1) on the way. To know hN, you must compute all N hashes. Every one of them. In order.
want h1000000000 ? ┌─────────────────────────────────────────────────────────┐ │ no formula, no lookup, no leap — │ │ the ONLY path is: h0 → h1 → h2 → … → h999999999 → hN │ └─────────────────────────────────────────────────────────┘Compare this to a clock you can fast-forward. If time were t = start + N × step, anyone could name the state at step one billion instantly by arithmetic — and so could a liar. The value of the hash chain is precisely that it has no such arithmetic. The only way to hold hN in your hand is to have paid for it, one hash at a time.
Under the hood — why a thousand cores don’t help
Section titled “Under the hood — why a thousand cores don’t help”Sequentiality and un-parallelizability are the same property viewed from two angles. Each step needs the previous step’s output as its input:
h_next = sha256(h_prev) ▲ │ └───────┘ the output you need feeds the input you haveThere is nothing for a second core to do. It cannot start computing h500 because it does not yet have h499 — and it cannot get h499 except by computing h498 first, which is the same problem one step down. A data dependency chains all billion steps into a single unbreakable line. Ten cores, a thousand cores, a warehouse of GPUs: the chain still advances at exactly the speed of one core running SHA-256 as fast as it can. You cannot buy your way to the future with more hardware. You can only wait for the hashes to happen.
This is worth pausing on because it is the exact opposite of most of what makes a computer fast. Everywhere else in this book, “throw more cores at it” is the answer — and on a later page we will use exactly that trick to check the clock in parallel. But producing the clock is the one place where parallelism is powerless, and that powerlessness is the whole point.
From a count of hashes to a count of seconds
Section titled “From a count of hashes to a count of seconds”Put the two facts together:
- Producing
hNrequiresNsequential hashes — no skipping, no parallelizing. - On a given machine, one hash takes a roughly fixed amount of time.
Therefore the count of hashes is a measure of how much sequential work has actually happened — and on known hardware, that converts directly to elapsed wall-clock time:
Two caveats keep this honest. First, the conversion to seconds is only as steady as the producer’s hashrate — a faster or slower machine reads a different wall-clock time off the same count, which is why Solana calibrates hashes-per-tick against reference hardware rather than trusting any one validator’s clock. Second, and more importantly, the count’s real product is not duration but order: a total, tamper-evident ordering of everything stamped into the chain, established with no timestamps and no agreement protocol at all.
Naming the concept: a verifiable delay function
Section titled “Naming the concept: a verifiable delay function”What we have built has a name. A construction that:
- takes a prescribed amount of sequential work to evaluate (you must do all
Nsteps), - cannot be sped up by parallelism (the steps chain), and
- produces a result whose position is a forgery-proof ordinal (you can’t have
hNwithout having paid for it),
is called a verifiable delay function, or VDF. The “delay” is the unavoidable sequential wait; the “verifiable” is that the result carries proof of the wait. VDFs were formalized by Boneh, Bonneau, Bünz, and Fisch (CRYPTO 2018) at almost the same time Anatoly Yakovenko introduced Proof of History in the 2017 Solana whitepaper. PoH is the practical embodiment of the idea: a delay you can read a clock off of.
The architect’s lens
Section titled “The architect’s lens”- Why does it exist? Because a global state machine needs a shared notion of “before and after,” and in a distributed, adversarial network there is no wall clock anyone can trust. A hash chain manufactures an ordering that requires no trust and no conversation — you verify it from the data.
- What problem does it solve? It removes ordering from the set of things validators must negotiate. Instead of exchanging “when did you see this?” messages, the order is a property baked into a chain anyone can recompute, which is what lets the pipeline run at hardware speed.
- What are the trade-offs? Production is stuck at one core’s hashing speed — you cannot make the clock tick faster by adding hardware — and verification, while parallel, is not cheaper than production the way a strict VDF’s would be. You also inherit a security assumption on SHA-256 rather than a proof.
- When should I avoid it? When you don’t actually need a verifiable ordering — a system with a single trusted sequencer, or one that tolerates loose ordering, gets no benefit and pays the sequential-hashing cost for nothing. It is also the wrong tool if you need the delay itself to be provably enforced against a cryptographic breakthrough.
- What breaks if I remove it? Ordering collapses back into a consensus sub-problem: validators must gossip and vote to agree on sequence before they can agree on anything else, reintroducing exactly the chatty bottleneck that order-first, vote-second was designed to escape.
Check your understanding
Section titled “Check your understanding”- Given only the
seed, why is computing allNhashes the only way to learnhN? Which property of SHA-256 rules out a shortcut? - A validator adds 999 more CPU cores to its machine. How much faster can it now produce the PoH chain, and why?
- Explain the chain of reasoning that turns “a count of hashes” into “a measure of elapsed time.” What extra fact about the hardware does it require, and what can the count still tell you even without that fact?
- What three properties make a construction a verifiable delay function, and which one is really just “un-parallelizable production” restated?
- The page insists iterated SHA-256 is “VDF-like” rather than a strict VDF. Name the two ways it falls short of the textbook definition, and state what PoH’s security ultimately rests on.
Show answers
- Because SHA-256 is pre-image resistant, there is no known way to invert an output or to compute the result of
Niterations by any route other than actually iterating. There is no closed-formf(seed, N)and no way to run the chain backwards, so the only path tohNish0 → h1 → … → hN, one step at a time. - Not at all — production stays at exactly one core’s hashing speed. Each hash needs the previous hash as its input (
h_next = sha256(h_prev)), so the steps form an unbreakable data-dependency chain and there is nothing for the extra cores to compute ahead of time. - (1) Producing
hNforcesNsequential hashes with no shortcut. (2) On a given machine each hash costs a roughly fixed time. Soelapsed ≈ hash_count / hashrate. The extra fact required is the hashrate of the producing hardware. Even without knowing the hashrate, the count still gives every stamped event an exact, tamper-evident ordinal position (which came before which). - A VDF (a) takes a prescribed amount of sequential work to evaluate, (b) cannot be parallelized to go faster, and (c) yields a result that is a forgery-proof ordinal you can’t hold without having paid for it. Property (b) is the un-parallelizable-production fact restated — it is what forces the delay to be real elapsed sequential work.
- First, a strict VDF has verification asymptotically cheaper than evaluation; PoH verification does the same number of hashes (it is only parallelizable across segments, not fundamentally cheaper). Second, its delay guarantee is not proven — it rests on the assumption that SHA-256 has no known shortcut faster than recomputing the chain. That assumption, not a theorem, is what the security ultimately rests on.