Skip to content

Banking and PoH: Stamping Order Into the Stream

The previous page, The TPU: The Leader’s Assembly Line, walked a transaction from the leader’s network card up through fetch, signature verification, and scheduling — right to the door of the stage where it finally runs. This page opens that door. It is where two machines that we have so far studied separately — the parallel runtime (banking) and the verifiable clock (PoH) — are wired together into a single loop, and where a block is not assembled and then stamped, but stamped as it is assembled.

That distinction is the whole point of the page. A naïve blockchain executes a block, freezes it, then computes a hash over the finished block. Solana refuses to wait: the leader interleaves execution with the clock so that the hashes of processed transactions are folded into the PoH chain the instant they succeed. The result is a stream of entries flowing out continuously, each one already carrying a provable position in time — the throughline of this book made mechanical: run the state machine at hardware speed by never stopping to draw a line around a block.

Recall what each half already knows how to do on its own.

Banking is the execution stage — Solana’s Sealevel runtime, which you built up in the Sealevel part. Given a batch of transactions whose account footprints are declared up front, it decides validity (does this transfer overdraw? is the signature authorized for these writes?) and updates the account database. It is the part that answers “did this transaction happen, and what did it change?”

PoH is the clock — the sequential SHA-256 hash chain you built in the Proof of History part. It does exactly one thing: it establishes order and timing by hashing continuously (h = sha256(h)), and it lets you seal an event into the timeline by mixing that event’s hash into one step of the chain. It answers “in what provable position, relative to everything else, did this happen?”

Neither half is a blockchain by itself. Banking knows what is true but nothing about when; PoH knows when but nothing about what. Bolt them into one loop and you get the missing thing: an ordered ledger — a sequence of state changes with a tamper-proof position for each.

incoming batch ──► BANKING ──────► "these N succeeded, here are their hashes"
(from scheduler) decides validity │
updates accounts ▼
POH RECORDER ──► mix the batch's hash
advances clock into the chain
ENTRY emitted
(num_hashes, resulting hash, mixin)

The unit that comes out of this loop is the entry. Everything else on the page is about what an entry is and why emitting a stream of them — rather than one finished block — is what lets the leader run flat out.

The entry: a batch of transactions plus its place in time

Section titled “The entry: a batch of transactions plus its place in time”

An entry is a small, self-describing checkpoint. In the book’s companion crate, rust/solmini/src/poh.rs, it is exactly this:

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Entry {
pub num_hashes: u64, // hashes since the previous entry (>= 1)
pub hash: Hash, // chain state right after this entry
pub mixin: Option<Hash>, // the event folded in at the last hash, if any
}

Three fields, and each earns its place:

  • num_hashes is how much sequential work separates this entry from the previous one — the amount of clock that ticked while this batch was being prepared. It is what makes an entry a measure of elapsed time, not just a label.
  • hash is the chain state right after this entry. Because it pins the boundary, a verifier knows both ends of every segment and can check segments in parallel (the asymmetry you proved in Verifying the Clock).
  • mixin is the transactions’ fingerprint. None means a bare tick — “time passed, nothing happened.” Some(h) means this entry recorded a batch: h is the hash of the transactions that were sealed here.

The record method is where a processed batch becomes an entry. It does n - 1 bare hashes, then one hash that folds the batch’s fingerprint into the chain:

pub fn record(&mut self, n: u64, data: &[u8]) -> Entry {
for _ in 0..n - 1 { // bare clock ticks while banking worked
self.state = hash_once(&self.state); // h = sha256(h)
self.count += 1;
}
let mix = mixin_of(data); // mix = sha256(batch of transactions)
self.state = hash_with(&self.state, &mix); // h = sha256(h || mix) ← the seal
self.count += 1;
Entry { num_hashes: n, hash: self.state, mixin: Some(mix) }
}

The single line hash_with(&self.state, &mix) is the entire act of “stamping order into the stream.” From that hash forward, every future state of the chain depends on this batch. The batch cannot be moved earlier or later, cannot be dropped, and cannot be swapped with a neighbor, without redoing all the sequential work that follows it. Order is not asserted by a field someone could edit — it is welded into the structure of the chain. (This is exactly the reordering-is-impossible property the solmini tests pin down: swap two recorded events and verify fails.)

Why interleave instead of finalize-then-hash

Section titled “Why interleave instead of finalize-then-hash”

Here is the design decision at the center of the page, stated as a fork in the road.

Option A — finalize, then hash (the classic block). Collect transactions for the whole slot, execute them all, freeze the resulting block, then compute one hash over the finished block. Simple, and it is what most chains do. But notice the cost: nothing leaves the leader until the slot is over. The clock, the ordering, and the block hash are all computed at the end, in one lump. Downstream validators sit idle for the whole slot, then receive a wall of data all at once.

Option B — stamp as you go (Solana). Run each batch the instant the scheduler hands it over, and the moment it succeeds, mix its fingerprint into the ever-running PoH chain and emit an entry. The clock never pauses; entries flow out the door continuously, in order, throughout the slot.

Option A (finalize-then-hash):
|==================== execute whole block ====================| hash | send
└──────────────── downstream waits this entire time ─────────┘
Option B (stamp as you go):
|exec|record|exec|record|exec|tick|exec|record|exec|record| …
└► entry └► entry └► entry └► entry └► entry (streaming out the whole slot)

Option B wins for three linked reasons, all of them the same idea seen from different angles:

  1. The clock is decoupled from finalization. PoH is always hashing — even during idle microseconds it emits bare ticks (mixin: None). So order and timing are being established continuously and do not have to wait for the batch, the block, or a vote. You are never blocked on “is the block done yet?” to advance time.

  2. Results stream out, so propagation overlaps execution. Because an entry is complete the moment it is recorded, the leader can hand it straight to block propagation (Turbine) while it is still executing later batches. Downstream validators start verifying and replaying the front of the slot before the leader has finished the back of it. Finalize-then-hash makes that overlap impossible by construction.

  3. Ordering never becomes a separate round. In a finalize-then-hash world you still owe an answer to “in what order did these happen?”, and on many chains that is its own negotiation. Here the order is the position in the entry stream — a free by-product of having recorded each batch as it ran. There is no discrete “ordering round” to run because ordering happened inline.

The mental upgrade this page asks for: stop picturing a block as a box you fill and then seal, and start picturing it as a segment of an unbroken stream. A block is just “all the entries produced during one slot.” The stream is primary; the block is a retrospective bracket around a slice of it.

Under the hood — the division of labor, precisely

Section titled “Under the hood — the division of labor, precisely”

It is worth being exact about who decides what, because the two halves are deliberately not symmetric.

ConcernOwnerWhat it produces
Is this transaction valid? (signatures, balances, program logic)Banking (Sealevel runtime)success/failure per transaction
What does the account state become?Bankingnew account values, committed on success
In what provable order did these run?PoH recorderposition in the hash chain
How much time elapsed?PoH recordernum_hashes per entry
The durable output both agree ontogetheran entry (ordered batch of results)

The ordering matters in one subtle way: banking decides success first, PoH records second. Only transactions that succeeded get their fingerprint mixed into the chain — a batch that fails validation never earns a position in time. So the entry stream is not “everything the leader attempted,” it is “everything the leader executed and committed, in the exact order it committed them.” PoH is the notary that stamps banking’s verdict; it never overrules it.

There is a real coupling here that the table hides: the PoH recorder is the gatekeeper of the slot. The banking stage cannot record an entry after the slot’s tick budget is exhausted — the recorder will reject it because the clock has moved past the slot boundary. So banking and PoH share a clock in a hard sense: banking must finish and record a batch before PoH ticks past the end of the slot, or that work is wasted and rolls over to the next leader. That constraint is the subject of the last section.

A slot is a fixed budget of clock — keep the pipeline full

Section titled “A slot is a fixed budget of clock — keep the pipeline full”

Recall from Ticks, Slots, and Hashes-per-Tick that a slot is not a number of transactions or a wall-clock timer the leader controls. A slot is a fixed count of ticks, and a tick is a fixed count of hashes. So a slot is a fixed amount of sequential PoH work — and because PoH is always hashing, that budget drains at a steady rate no matter what banking is doing.

This flips the leader’s incentive. The clock will spend the slot’s ticks whether or not there is work to stamp into them. Every tick that passes carrying mixin: None is a slice of the leader’s slot spent on nothing — provable idle time. So the leader’s job during its slot is a race to keep banking producing successful batches fast enough that most entries are record entries, not tick entries.

one slot = fixed tick budget (drains at PoH's rate, always)
well-fed leader: record record record tick record record record ← mostly work
starved leader: record tick tick tick tick record tick ← mostly idle ticks
└──────── slot capacity wasted ────────┘

Two ways to waste a slot, and both are pipeline-starvation:

  • Not enough transactions arrive. If the scheduler has nothing to hand banking, PoH keeps ticking and the entries are bare ticks. Solana’s answer to this is upstream: because the leader schedule is known in advance, clients forward transactions directly to the upcoming leader (Gulf Stream) so the leader’s inbox is already full when its slot begins.
  • Banking can’t keep up. If execution is too slow — too many conflicts serializing onto a hot account, or the batches are compute-heavy — the scheduler backs up and the recorder ticks past the work anyway. This is why the runtime executes conflict-free batches in parallel (Executing a Batch in Parallel): the whole point of parallel execution is to feed the recorder fast enough to fill the slot with records instead of ticks.

So the two stages this page unifies are also the two things that must be balanced: PoH sets a merciless, hardware-paced deadline, and banking must run at hardware speed to meet it. A full pipeline is one where banking is fast enough that PoH almost never has to emit a bare tick during a busy network — the entire validator pipeline exists to keep that pipe pressurized.

  • Why does it exist? Because order and execution are two different questions, and answering them in one interleaved loop lets a leader emit an ordered ledger continuously instead of finalizing a block and then stamping it. It is how “run the global state machine at hardware speed” stops being a slogan and becomes a pipeline that never stalls to draw a block boundary.
  • What problem does it solve? It removes finalization from the critical path. Ordering, timing, and execution advance together as a stream of entries, so propagation can overlap execution and no separate “agree on the order” round is ever needed — the order is the entry sequence.
  • What are the trade-offs? The leader is now bound by a merciless clock: a slot is a fixed tick budget, and any batch not recorded before the slot ends is wasted work. That forces the whole upstream pipeline (Gulf Stream forwarding, parallel banking) to exist just to keep the recorder fed, and it makes a slow or overloaded leader silently waste slot capacity on bare ticks.
  • When should I avoid it? If you don’t need a continuously advancing, hardware-paced global clock — a single node, or a small trusted cluster with a shared wall clock and low throughput — interleaving execution with a hash chain is pure complexity. The classic execute-then-hash block is simpler and fine when you can afford to wait a whole round.
  • What breaks if I remove it? Split banking and PoH back apart and you lose streaming: the leader must finalize a block before it can be hashed and sent, downstream validators idle for a full slot instead of replaying the front of it, and ordering becomes a separate problem to solve again. You would have traded a continuous stream for discrete rounds — exactly the bottleneck the architecture was built to avoid.
  1. Banking and PoH each answer a different question about a transaction. State both questions, and explain why neither half is a ledger without the other.
  2. What are the three fields of an Entry, and what does each contribute? In particular, what is the difference between an entry with mixin: None and one with mixin: Some(h)?
  3. Contrast “finalize-then-hash” with “stamp as you go.” Give two concrete things the streaming approach makes possible that the finalize-first approach cannot.
  4. The page says “banking decides success first, PoH records second.” Why does that ordering matter — what would go wrong if PoH stamped a batch before banking confirmed it?
  5. A slot is a fixed budget of PoH ticks that drains at a steady rate. Explain the two distinct ways a leader can end up “wasting” a slot, and name the mechanism elsewhere in the pipeline that each waste mode is meant to prevent.
Show answers
  1. Banking answers “is this transaction valid, and what did it change?” — it decides validity and updates the account state. PoH answers “in what provable position, relative to everything else, did this happen?” — it establishes order and timing. Banking alone knows what is true but nothing about when or in what order; PoH alone knows when but nothing about what. Only together do they produce an ordered ledger: state changes each with a tamper-proof position.
  2. num_hashes — sequential work (elapsed clock) since the previous entry; hash — the chain state right after this entry, which pins the segment boundary so verification can be parallel; mixin — the event sealed here. mixin: None is a bare tick (“time passed, nothing happened”); mixin: Some(h) is a record, where h is the fingerprint of the batch of transactions folded into the chain at this step, giving that batch a provable position.
  3. Finalize-then-hash executes the whole block, freezes it, then hashes the finished block — so nothing leaves the leader until the slot is over. Stamp-as-you-go records each batch into the running PoH chain the moment it succeeds and emits an entry immediately. Two things streaming enables: (a) propagation overlaps execution — entries can be handed to Turbine while later batches are still running; and (b) no separate ordering round — order is just the position in the entry stream, a by-product of recording each batch inline rather than a negotiation run at the end. (Also acceptable: the clock never pauses, advancing via bare ticks even when idle.)
  4. Because only successful transactions should earn a position in time. Banking commits state on success, then PoH stamps that verdict; PoH is a notary, not a judge. If PoH stamped a batch before banking confirmed it, the entry stream would contain transactions that failed validation or overdrew an account — the “ordered ledger” would include changes that never legitimately happened, and downstream verifiers replaying the entries would compute a different (or invalid) state.
  5. (a) Not enough transactions arrive, so the scheduler has nothing to hand banking and PoH fills the slot with bare ticks — prevented upstream by Gulf Stream, which forwards transactions directly to the known upcoming leader so its inbox is full when the slot starts. (b) Banking can’t keep up (conflicts serializing, heavy compute), so the recorder ticks past the work — prevented by parallel execution in Sealevel, which runs conflict-free batches across cores to feed the recorder fast enough to fill the slot with records instead of ticks.