Proof of History

Proof of History (PoH) is Solana's most distinctive innovation—a cryptographic clock that establishes the passage of time without requiring nodes to communicate.

The Problem: Time in Distributed Systems

In distributed systems, agreeing on time is surprisingly difficult:

Text
Traditional consensus:
Node A: "I have transaction at 10:00:00.001"
Node B: "I have transaction at 10:00:00.002"
Node C: "I have transaction at 10:00:00.001"

Problems:
- Clocks drift (even atomic clocks)
- Network latency varies
- Malicious actors can lie about timestamps
- No way to verify time claims without communication

Why Time Matters for Blockchains

Without reliable time:

  • Can't order transactions without voting
  • Voting requires network round-trips
  • Round-trips limit throughput
  • Consensus becomes the bottleneck

What Is Proof of History?

PoH is a Verifiable Delay Function (VDF)—a computation that:

  1. Takes a predictable amount of time
  2. Produces a provably unique output
  3. Cannot be parallelized

The Core Insight

Sequential hashing creates a "clock":

Text
hash_0 = hash("genesis")
hash_1 = hash(hash_0)
hash_2 = hash(hash_1)
hash_3 = hash(hash_2)
...
hash_n = hash(hash_n-1)

Each hash MUST come after the previous one.
The sequence proves time has passed.

Why This Works

SHA-256 properties ensure:

  • Sequential dependency: hash_n requires hash_n-1
  • Cannot parallelize: No shortcut to compute hash_n
  • Deterministic: Same input always produces same output
  • Verifiable: Anyone can check the sequence

How PoH Works

Basic PoH Generation

TypeScript
interface PoHEntry {
  hash: Buffer; // Current hash
  numHashes: number; // Hashes since last event
  transactions?: Transaction[]; // Events mixed in
}

function generatePoH(
  previousHash: Buffer,
  hashesPerTick: number,
  events?: Transaction[],
): PoHEntry {
  let hash = previousHash;
  let numHashes = 0;

  // Hash continuously
  for (let i = 0; i < hashesPerTick; i++) {
    hash = sha256(hash);
    numHashes++;

    // Mix in events when they arrive
    if (events && i === Math.floor(hashesPerTick / 2)) {
      const eventHash = computeEventHash(events);
      hash = sha256(Buffer.concat([hash, eventHash]));
    }
  }

  return { hash, numHashes, transactions: events };
}

Mixing In Events (Transactions)

Events are "watermarked" into the PoH sequence:

Text
PoH sequence without events:
H0H1H2H3H4H5H6H7

Event occurs between H3 and H4:
H0H1H2H3[Tx]H4' → H5'H6' → H7'

Where: H4' = hash(H3 || hash(Tx))

The event is now PROVABLY ordered:
- After H3 (3000 hashes from start)
- Before H4' (3001 hashes from start)

Visual Timeline

Text
Time ────────────────────────────────────────────────────────►

     ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
PoH:H0│→│ H1│→│ H2│→│ H3│→│ H4│→│ H5│→│ H6│→│ H7│→ ...
     └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
                         │           │
                         ▼           ▼
                      ┌─────┐    ┌─────┐
Events:Tx1 │    │ Tx2 │
                      └─────┘    └─────┘
                         │           │
                      count=     count=
                      3000       5000

Tx1 provably happened BEFORE Tx2
(separated by 2000 hashes ≈ X microseconds)

PoH Verification

The beauty of PoH: verification is parallelizable even though generation isn't.

Verification Strategy

Text
Generation: SEQUENTIAL (single CPU core)
H0H1H2H3H4H5H6H7

Verification: PARALLEL (many CPU cores)
Core 1: Verify H0H1H2
Core 2: Verify H3H4H5
Core 3: Verify H6H7H8
Core 4: Verify H9H10H11
...
Then verify boundaries match

Verification Code

TypeScript
async function verifyPoHSequence(entries: PoHEntry[]): Promise<boolean> {
  // Split work across CPU cores
  const numCores = navigator.hardwareConcurrency || 4;
  const chunkSize = Math.ceil(entries.length / numCores);

  const verificationPromises = [];

  for (let i = 0; i < numCores; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, entries.length);
    const chunk = entries.slice(start, end);

    verificationPromises.push(
      verifyChunk(chunk, i === 0 ? null : entries[start - 1].hash),
    );
  }

  const results = await Promise.all(verificationPromises);
  return results.every((r) => r === true);
}

function verifyChunk(
  entries: PoHEntry[],
  expectedStartHash: Buffer | null,
): boolean {
  let hash = expectedStartHash;

  for (const entry of entries) {
    // Verify hash chain
    for (let i = 0; i < entry.numHashes; i++) {
      if (entry.transactions && i === Math.floor(entry.numHashes / 2)) {
        const eventHash = computeEventHash(entry.transactions);
        hash = sha256(Buffer.concat([hash!, eventHash]));
      } else {
        hash = sha256(hash!);
      }
    }

    if (!hash.equals(entry.hash)) {
      return false;
    }
  }

  return true;
}

PoH and Consensus

PoH doesn't replace consensus—it enhances it.

Tower BFT + PoH

Text
Without PoH (Traditional BFT):
─────────────────────────────────────────────────
1. Leader proposes block
2. Validators receive block
3. Validators wait for timeout (ensure they've heard from everyone)
4. Validators vote
5. Wait for vote collection timeout
6. Finalize

Problem: Waiting for timeouts limits speed

With PoH:
─────────────────────────────────────────────────
1. Leader produces block with PoH entries
2. Validators receive block
3. Validators verify PoH (no waiting!)
4. Validators vote immediately
5. PoH provides timeouts automatically
6. Finalize

No network timeouts needed = faster finality

Vote Timing with PoH

Rust
// Conceptual vote logic
fn should_vote(
    current_poh: &PoHHash,
    last_voted_poh: &PoHHash,
    min_hashes_between_votes: u64
) -> bool {
    let hashes_elapsed = current_poh.count - last_voted_poh.count;

    // PoH provides reliable timing without network communication
    hashes_elapsed >= min_hashes_between_votes
}

Performance Impact

Throughput Gains

Text
Traditional consensus limited by:
- Network round-trip time: ~100ms between regions
- Need 2-3 rounds for consensus
- Practical limit: ~100 TPS

With PoH:
- No waiting for network timing
- Single round sufficient
- PoH provides ordering
- Limit: Hardware/bandwidth

Theoretical improvement: 100x - 1000x

Actual Numbers

MetricPoH Impact
Block time400ms (limited by propagation, not consensus)
Tx orderingSub-millisecond precision
Verification25,000 hashes/second per core
Historical proofO(1) lookup in verified sequence

PoH Implementation Details

Tick Rate

Solana's PoH runs at approximately:

  • 400,000 hashes per slot
  • 160ms per tick
  • 64 ticks per slot
  • 400ms per slot
Text
One Slot (400ms):
├── Tick 0 ────────── Tick 1 ────────── ... ────────── Tick 636250 hashes       6250 hashes                      6250 hashes
└────────────────────────────────────────────────────────────────┘
                        400,000 total hashes

PoH in Block Structure

Text
Solana Block (Slot):
┌────────────────────────────────────────────────────────────────┐
│ Slot Header                                                    │
│ - Slot number                                                  │
│ - Parent slot hash                                             │
│ - PoH hash (at slot start)                                     │
├────────────────────────────────────────────────────────────────┤
│ Entry 0                                                        │
│ - PoH hash                                                     │
│ - Num hashes since last entry                                  │
│ - Transactions []                                              │
├────────────────────────────────────────────────────────────────┤
│ Entry 1                                                        │
│ - PoH hash                                                     │
│ - Num hashes since Entry 0                                     │
│ - Transactions []                                              │
├────────────────────────────────────────────────────────────────┤
│ ...                                                            │
├────────────────────────────────────────────────────────────────┤
│ Entry N (Last Tick)                                            │
│ - Final PoH hash for this slot                                 │
│ - is_tick: true                                                │
└────────────────────────────────────────────────────────────────┘

Common Misconceptions

"PoH is a consensus mechanism"

Reality: PoH is a clock, not consensus. Tower BFT is the consensus mechanism. PoH makes Tower BFT faster by providing timestamps without network round-trips.

"PoH proves things happened at specific real-world times"

Reality: PoH proves ordering and duration in "PoH time" (hash counts). Mapping to real-world time requires knowing the hash rate of the generator.

"PoH is similar to Bitcoin's proof of work"

Reality:

  • PoW: Finding a hash below a target (probabilistic, competitive)
  • PoH: Sequential hashing (deterministic, non-competitive)
  • PoW proves work was done; PoH proves time passed

Security Considerations

What If Someone Pre-computes PoH?

They can't gain advantage:

Text
Scenario: Attacker pre-computes 1 hour of PoH

Problem: They don't know what transactions will arrive
- Transactions include signatures on recent PoH hashes
- Transactions with wrong PoH hash are rejected
- Pre-computed PoH has no valid transactions to mix in

Result: Pre-computation is useless

What About Faster Hardware?

Faster PoH generators could theoretically gain advantage:

Text
Defense 1: GPU/ASIC resistance
- SHA-256 is relatively ASIC-resistant
- Sequential nature prevents parallelization

Defense 2: PoH rate limits
- Network enforces expected PoH rates
- Too-fast PoH is suspicious

Defense 3: Validator set economics
- Becoming a leader requires stake
- Attack cost = stake at risk

Key Takeaways

  1. PoH is a cryptographic clock based on sequential hashing
  2. Generation is sequential but verification is parallel
  3. Events are watermarked into the PoH sequence with provable ordering
  4. PoH enables fast consensus by eliminating network timeout requirements
  5. PoH doesn't replace consensus—it makes consensus faster

Deep Dive: VDF Mathematics

Verifiable Delay Functions (VDFs) like PoH have three properties:

Text
1. Sequentiality:
   f(x) cannot be computed faster than T steps
   Even with unlimited parallel processors

2. Uniqueness:
   For any input x, there's exactly one valid output y

3. Efficient Verification:
   Verifying f(x) = y takes << T steps

SHA-256 chain satisfies these:
1. Sequential: Each hash needs previous hash
2. Unique: SHA-256 is deterministic
3. Efficient: Verification parallelizes

Try It Yourself

  1. Compute PoH manually: Hash "genesis" 10,000 times. How long does it take? Can you parallelize it?

  2. Verify PoH: Given a sequence of hashes, write code to verify it in parallel.

  3. Calculate PoH time: If Solana does 400,000 hashes per 400ms slot, what's the hash rate? How many hashes occur during your transaction's journey?


Next: Slots & Epochs - How Solana organizes time into slots and epochs.