# BFT finality over checkpoint gossip Optional permissioned finality layered on the checkpoint chain. Gossip alone detects divergence after the fact; a validator set prevents it from being presented as truth in the first place: a height commits to exactly one head, and everyone can check that commitment offline. --- ## 1. Roles - **Validators** are relays whose transport key doubles as their validator key. One key, one role, no new secrets. The set is fixed in configuration (`-bft-validators`, `-bft-urls`) — membership changes are an operational ceremony, not a protocol event. - **Everyone else** (relays, light nodes, verifiers) needs only the public validator list to check a certificate. ## 2. Protocol Per height `h` (aligned with checkpoint epochs), deterministic leader: ``` proposer(h, r) = validators[ SHA-256(height‖round)[0] mod n ] ``` 1. The round's proposer offers its latest head. 2. Validators **prevote** for the proposed head (or nothing). 3. On a quorum prevote for X, each validator **precommits** X. 4. On a quorum precommit for X the height finalizes with a *certificate*: the collected precommit signatures. Height advances; the finalized head becomes the anchor every future proposal must chain onto. Quorum is `2f+1` of `n` with tolerated Byzantine validators `f = ⌊(n−1)/3⌋`. A stalled or equivocating proposer costs one round timeout, then leadership rotates. All messages are canonical bytes signed with Ed25519; phases live in the signing domain, so a prevote can never be replayed as a precommit. ## 3. Guarantees and simplifications Safety: two certificates for one height require ≥⅓ Byzantine validators. Liveness: finality proceeds while ≥ quorum validators are online and honest; a minority outage stalls that height until they return (fail-stop, never fork). Documented v1 simplifications, none safety-relevant: - Single in-flight round per height on the driver; rotation happens through timeouts rather than full Tendermint lock-rules. - Validators accept a proposal whose head they have already verified via gossip; head-body re-fetch before prevoting is future work. - Validator-set changes are manual: update config, restart. ## 4. Endpoints | Method | Path | Purpose | |--------|-------------------------------|--------------------------------------------| | POST | `/v1/bft/proposal` | validator fan-out: signed proposal | | POST | `/v1/bft/vote` | validator fan-out: signed prevote/precommit| | GET | `/v1/bft/state` | `{enabled, height, last_finalized}` | | GET | `/v1/bft/certificate/{h}` | finality certificate for a height | Light nodes pin the validator keys (`BFTValidators`); `/v1/bft/certificate` then serves only certificates that verify against exactly those keys, so a light node can demand "quorum-agreed root" instead of trusting any single mirror's checkpoint. ## 5. Composition ``` relays ──gossip──▶ heads ──▶ validators vote ──▶ certificate │ light nodes ◀── verify offline ──────────────┘ ``` Gossip remains the data plane; BFT is a thin consensus overlay on which head counts as canon per height. Disabling it returns the system to plain checkpoint-gossip with split-view detection by comparison.