DOCS / PROTOCOL / PROOF OF HISTORY

Proof of History

Proof of History (PoH) is a verifiable delay function based on iterated SHA-256. ShardoChain uses it as a shared clock — a sequence of hashes every validator can compute and verify independently — so leader rotation and slot timing don't require consensus round-trips.

What PoH is, and is not

PoH is not a consensus protocol. The fork-choice rule and safety guarantees come from HotStuff-2. PoH provides:

The verifiable delay function

The unit is PohHash = 32-byte SHA-256. Two operations:

impl PohHash {
    /// Advance the chain by one step: SHA-256(self).
    pub fn next(&self) -> Self;

    /// Mix external data: SHA-256(self || data).
    pub fn mix(&self, data: &[u8]) -> Self;
}

next() is not parallelisable — to reach the N-th hash you must compute the previous N-1 in sequence. A chain of K iterations is a mathematical proof of K / hashes_per_second wall-clock time, verifiable by any observer.

Configuration

ParameterTestnetMainnetMeaning
slot_duration_ms700800Target wall-clock slot duration
ticks_per_slot88Tick subdivisions of one slot
hashes_per_tick12 50012 500SHA-256 iterations per tick (calibrated for x86-64 SHA-NI)
slots_per_epoch432 000432 000~80 h per epoch

With slot_duration_ms = 700, theoretical max throughput is 1.43 blocks/second. Multiple v5.1.x soaks have sustained this rate for hours.

PoH and block production

The leader for (height, view) is:

fn leader_for(set: &[Validator], height: u64, view: u64) -> Address {
    let idx = (height + view) % set.len();
    set[idx].address
}

Each validator's local PoH recorder advances by tick() once per tick_duration_ms ≈ 87 ms. After 8 ticks, the slot increments. At slot start, the validator checks if it's the leader — if so, drains the mempool, executes, signs the block.

PoH vote

struct PohVote {
    slot: u64,
    block_hash: Hash256,
    voter: Address,
    signature: [u8; 64],   // over borsh(slot, block_hash, voter, view)
    view: u64,                // v5.1.0+: prevents cross-view replay
}

The signature covers view so a vote at view 0 cannot be replayed at view 1.

Persistence

The PohVoteRegistry snapshots itself to data_dir/vote_registry.bin every ~5 s with retention 512 slots (~6 min). On restart, the snapshot is loaded before the first slot fires. Without this (pre-v5.0.47) a restarted validator would start with an empty registry and the cluster would stall waiting for parent-quorum reconstruction.

v5.1.8 added explicit fsync on the temp file plus parent-directory fsync after rename, making the snapshot durable across power loss.

Slot tiebreak in fork-choice

Under sustained burst, two competing blocks at the same height with equivalent QC weight can emerge (BFT safety violation). PoH solves this deterministically: the block with the lower slot wins — every validator sees the same slot for the same hash because slots are time-derived. Implemented in fork_detector.rs::fork_choice_rule (v5.0.32, commit 2fdb3f7).

Light-client verification

shardo-poh::verifier::verify_chain replays the chain from a known checkpoint to a target hash and counts SHA-256 iterations. The delta is a mathematical proof of elapsed time. Cost is O(N) hashes — verifying 1 hour of testnet (~5 142 slots × 8 ticks × 12 500 hash ≈ 514 M hashes) takes ~30 s on a SHA-NI-capable CPU.

Known limitations