Files
consensus/engine
zeekayandHanzo Dev 80fd00223d feat(chain): keep finality certs on disk, so a peer catching up can still be served
A node that fell behind cannot rejoin by voting — the network will not re-vote a
decided height — so AcceptCatchupBlock finalizes each gap block exclusively by
running a SERVED cert through HandleIncomingCert. The set of heights a straggler
can recover is therefore the set its peers can still produce a cert for.

certBytesByBlock is a plain map built empty at New(), so that set was bounded by
process lifetime rather than by the window maxServedCerts names: recoverability
depended on how recently the peers happened to start, and a peer whose gap
predated their start could ask for finalized+1 indefinitely while the contiguity
guard refused the tip certs it was offered instead.

CertStore gives the cert the same durability as the block it proves. One file per
cert named "<height>-<blockID>", so the name is the whole index — lexical order is
height order, eviction follows the chain, and there is no separate index to fall
out of step with the directory. The node opens it per chain from the chain data
dir (chains/manager.go), beside the vote guard.

It fails OPEN where the vote guard fails closed, and the asymmetry is the point:
an unreadable cert costs a peer one fetch elsewhere and a corrupt one is refused
by HandleIncomingCert exactly as a forged one is, while unreadable equivocation
memory risks a fork. Nothing downstream trusts these bytes because they came from
disk.

The durable write runs OFF the engine lock, in persistServedCert — it is an
atomic replace with an fsync (~17ms measured) and finality must not wait on a
disk. The in-memory window has already answered for the height by then, so the
only exposure in the gap is losing the ability to serve one height.

writeFileAtomic is now the single atomic writer in the package, shared with the
vote guard so the two durable stores cannot drift on crash-safety.

Tests: cert_store_test.go — survives a restart, retains the newest window by
height at both entry points (recovery at open and the hot Put path), recovers the
window from names alone, leaves files it does not own untouched, and round-trips
the name across the uint64 edges. Negative control executed: removing the durable
read from CertForBlock fails TestCertOutlivesTheProcess and nothing else.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 18:18:53 -07:00
..
2025-12-19 08:49:11 -08:00