Files
consensus/engine/chain
zeekayandHanzo Dev a664c82534 engine/chain: v1.36 two-tier accept — Nova (majority) accept vs Quasar (⅔-stake) export
Split the single ⅔-stake accept threshold into two orthogonal finality-ladder tiers so a
3/5-live foundation keeps producing while certification pauses (the "survive 3/5, majority
is enough" mandate). Before, VM.Accept was gated on one ⅔ cert (bftAlpha = ⌊2n/3⌋+1); at
3/5 (60% < ⅔) that cert can never assemble and the fleet froze the instant a 2nd node dropped.

  Nova   = LOCAL ACCEPT.  NovaQuorum(n)=⌊n/2⌋+1 distinct signers; drives VM.Accept.
           Crash-fault-safe by majority intersection (stake-INDEPENDENT — do not add a
           stake gate or 3/5 liveness breaks when the absent minority holds the stake).
           NOT Byzantine-safe: a lone equivocator can fork a bare majority, so two Nova
           certs are possible (expected, non-exportable) and equivocation HALTS fail-closed
           via the ledger per-height gate (ErrHeightAlreadyFinalized) — never a silent EVM reorg.
  Quasar = EXPORT.  strict >⅔ of stake by distinct signers (config.TwoThirdsStakeFloor).
           Byzantine-fork-safe (2α−n>f). The ONLY tier bridges / DEX 0x9999 / cross-chain /
           validator-set transitions may consume.

Decomplected — one threshold per place:
- QuorumCert carries Tier (Nova|Quasar); VerifyWeighted selects the threshold by tier
  (majority-of-ValidatorCount for Nova, ⅔-by-stake for Quasar). The tier is NOT bound into
  the signed vote (votes are tier-agnostic), so it cannot be forged upward — VerifyWeighted
  re-derives the real threshold from the authoritative set. Fails closed on an unresolved set
  (n<1) so NovaQuorum(0)=1 can never let a lone node self-accept (the 1085013 fork class).
- assembleCertLocked assembles a NOVA cert (NovaQuorum) for the accept path; the zero-cert
  refusal (acceptWithCertCore) and the lone-finalizer resolved-set guard are preserved.
- A trailing QuasarAttestor (post-accept, engine/chain/attestation.go) assembles the ⅔-stake
  export cert from the SAME accept votes. Because the Nova cert freezes at the bare majority,
  the ⅔-th stake vote necessarily follows the accept, so finalized-block votes are routed to
  the attestor via a re-verified late-attestation path (handleVote / HandleIncomingVote).
- GetCertifiedTip → GetQuasarTip (highest ⅔-cert block, NEVER the Nova tip); GetNovaTip exposes
  the accepted tip. PromoteQuasar (single writer of the export frontier) validates each promotion
  against the Nova ledger, inheriting its single-canonical-per-height gate → no two conflicting
  Quasars. FinalityStatus surfaces nova/quasar/horizon heights + responsiveStakePct +
  certificateAvailable + degraded; a Nova height is never reported as certified.
- Incoming-cert threshold pre-check made tier-aware so gossiped Nova certs are not rejected.

Tests: nova_quasar_matrix_test.go covers the adversarial matrix (n∈{1..5}+weighted,
3/5-Nova-no-Quasar, Quasar-only-at-⅔, 3/5-partition, equivocation → two Nova possible but
never two Quasar, behind-node rejoin non-event, degraded RPC, export gates on Quasar). ~17
pre-existing RED safety tests re-framed from the accept tier to the export tier (a sub-⅔
coalition may Nova-accept locally but can never reach Quasar/export). Single-engine matrix is
-race clean; the in-process multinode convergence tests skip under -race (timing/FIPS-ed25519
concurrency, per the existing race_flag discipline).

Astrophysics ontology only — no new tendermint/prevote/precommit/viewchange/snowman terms.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-10 03:58:20 -07:00
..