docs/design/genesis-forward.md names the three fields, their defaults, the digest change and the gates; spec 03 W1 and W5 amended and the W5 implementation row filled; spec 01 the cache rung beside the schedule; spec 04 the class-group VDF's quantum fallback flagged, not sized; infra/fast-time/key-succession.mjs (3 nodes, one CPU miner each: the window fills, a succession carried once on every node, refused twice, scheme 1 refused by every node; the known-failed case --expect no-succession first); override-60x.json carries the four new keys at never. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
10 KiB
Genesis forward-compatibility: the scheme byte, key succession, the cache rung
7 October 2026, 10:1x UK, the project lead's order to build mission item 8 now (docs/analysis/mission/mission.md section 2.8; the research in docs/analysis/mission/future.md sections 1.3, 7 and 10 and docs/design/finality-in-proof.md section 7). Branch genesis-forward on the node fork (from release-0.3.19-node dc141409, the merged line that carries the ladder and the W7 leave item, which ca3-v4-0318 alone does not) and genesis-forward on the repo. Three genesis fields, every switch never on the devnet (its digest does not move), all three set at the testnet genesis by the testnet lane, which holds the cut and re-pins. The class-group VDF's quantum fallback is flagged in spec 04 section 4.8, not built.
1. The scheme byte
| Item | Place | Value |
|---|---|---|
| The byte | finality::Vote::sig_scheme, finality::KeyReveal::sig_scheme, finality::Succession::successor_scheme |
SIG_SCHEME_BLS12_381 = 0 everywhere today |
| The genesis value | Params::sig_scheme (override key sig_scheme) |
0 on every network |
| The switch | Params::sig_scheme_activation_daa (override key sig_scheme_activation_daa; u64::MAX = never) |
never on devnet, simnet, mainnet; 0 at the testnet genesis |
| The digest | both fields enter once the switch is set (the 0.3.15 rule) | the devnet's digest unchanged; the testnet's moves at the cut |
| The wire, plain | vote item tag 1 (Vote::LEN = 280 bytes), evidence tag 3, reveal IGNK + 288 hex: scheme 0 implied |
byte for byte what every live network carries |
| The wire, explicit | vote item tag 5 = scheme || vote, evidence tag 6 = scheme || first || second, reveal IGNS + 2 hex of the scheme + 288 hex |
written by every template once the switch is active (encode_section_explicit_within); a vote of any other scheme is always explicit, so a scheme the node does not run is never mistaken for one it does |
| The active scheme | igneum::active_sig_scheme(genesis, class) = the scheme the program class at the sink names (SIG_SCHEME_OF_CLASS, a genesis table with no row today), else the genesis byte; the finality manager re-reads it at every virtual change |
0 |
| The refusal | FinalityManager::scheme_refusal: a reveal is not registered, a vote is not recorded, carried or gossiped, a successor is refused; the RPC answers refused: vote carries signature scheme 1; the active scheme is 0 (BLS12-381); another scheme is named only by a program class the 95 percent signal moves to, and none names one |
every node, whatever its switch |
Why a class change names the scheme: the flip is the P2 mechanism (95 percent of mining weight over a window with a floor height, the one path a consensus change takes on this chain), and the aggregated-vote format must ship first (naive ML-DSA-44 votes at 8,192 voters cost 19.4 MB a checkpoint and 57 GB a day, future.md 7.3; with 250x SNARK aggregation about 223 MB a day). Adding a row to the table is a code change under the class path; the byte is in every item from genesis so the row costs no fork.
2. Key succession, W5
| Item | Place | Value |
|---|---|---|
| The item | finality::Succession (tag 7, 297 bytes): daa, old key, successor key, successor scheme, the old key's signature, the successor's signature, both over "igneum-succeed-v1/" || chain_id || 0 || daa || old || new || scheme under IGNEUM_SUCCEED_V1_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_ |
the successor's signature is its consent and its proof of possession in one |
| The switch | Params::finality_succession_activation_daa (override key of the same name) |
never everywhere; 0 at the testnet genesis; in the digest once set |
| Submission | submitFinalitySuccession (RPC op 158, gRPC 1131/1132, wRPC, igneum-miner succeed <grpc url> <old label> <new label>) |
carried after the leaves in the templates of the node that holds it; no p2p gossip kind (the successor's own node mines it) |
| The fold | successions_at (deterministic like leaves_at: the lowest-DAA carrier in C's past dates it) and fold_successions inside voters_at: the old key's window blocks are credited to the successor as they stand (count and oldest block), the old key leaves the table, a ban or a leave on the old key lands on the successor; the successor's presence counts the old key's carried votes |
from the first checkpoint whose past holds the carrier; the window inherited, not a fresh one |
| Once | one record per old key: a second succession from a key that handed over is refused (already handed its weight to), a successor that has itself handed over is refused (has itself handed), which also refuses every cycle; a chain old to new to newer is legal |
a record outlives the weight it moved by one window, then is trimmed |
| After | the old key's votes are refused (handed its weight to), never counted, never carried |
the old key is dead |
| The report | getFinalityWeights: succeededFrom on the successor's row, a weightless row for the old key with succeededTo |
both nodes of the unit test agree on every voter list |
Not in this round: a p2p gossip kind for successions (the leave's shape, protocol version bump); the app's "rotate key" button, which signs the item from the old key's label and restarts the miner under the new label (the succeed subcommand is the primitive); the aggregated-vote format.
3. The cache rung beside N
| Item | Place | Value |
|---|---|---|
| The rung | igneum::CacheRung { mib, admissible } as LatencyLadder::cache_rung (override key latency_ladder_cache_rung) |
{512, false} at genesis; a power of two above the 256 MiB genesis cache |
| The signal | LadderSignal::Cache = both ladder bits set (header version 0xc000; until today both set was "no signal" and no node ever stamped it, so no block written so far changes meaning); IGNEUM_LADDER_SIGNAL=cache |
code 3 in PowEpochInfo::latency_ladder_signal |
| The rule | latency_ladder_step_signalled_with_cache: the cache step moves 0 to 1 when every one of the newest seven windows reaches 90 percent for the cache rung, the rung is admissible and the cool-down holds (the oldest window begins after the last decision, shared with N); never back; never in the same decision as N (one signal per block, exclusive shares) |
LadderState::cache_step |
| The digest | the rung's size and flag enter with the ladder, once latency_ladder_activation_daa is set |
the testnet's digest moves at the cut |
| The RPC | powEpoch.latencyLadderCacheStep, nextLatencyLadderCacheStep, latencyLadderCacheMib, nextLatencyLadderCacheMib, latencyLadderCacheBps, latencyLadderCacheWeakestBps, latencyLadderCacheAdmissible (proto fields 37 to 43); the daemon's ladder line (cache [512 MiB]) |
the rung visible on every node |
| The gate | admissible in the genesis list, false until measured: the cold verify of one warp with the 512 MiB cache on the reference core with its SMT sibling loaded under 10 ms (the verifier reads the cache, so this is the bound), the day-cache build on a 2019-class core under twice today's, and the 8 GB tier still holding dataset, cache and the prover footprint |
the rule never enters an inadmissible rung (tested) |
Why beside N and not a seventh N rung: the six approved rungs and their indices stand (the project lead, 7 October 2026, 09:3x UK); a rung inserted in the list would either sit behind the three inadmissible rungs (unreachable) or shift the approved indices. A second lever on the same signal carrier keeps the list as approved and the cool-down shared.
Owed with the measurement: the engine's consumption of cache_mib (EpochSeeds carries shadow_reps today and no cache size; the pack and the three hosts build a 256 MiB cache), so the flag stays false until the path exists and is measured. Per tier what the rung means: every tier from 8 GB holds a 512 MiB cache; the day-cache build doubles (about 0.7 s on the reference core today, approximate, from the class v4 fill line); the Apple tier's unified memory holds it; a pool user does nothing.
4. The class-group VDF under a quantum computer
Flagged in spec 04 section 4.8, not sized: Shor computes the class-group order, which removes the sequentiality assumption of the Wesolowski VDF, so an attacker with a cryptographically relevant quantum computer grinds the hourly seed (a liveness nuisance against the lottery, not a safety break; finality rests on the vote keys of section 1). The fallback is a hash-chain delay behind the same version byte, designed when the scheme flip is scheduled.
5. Gates
| Gate | Where | Result |
|---|---|---|
| The digest test | consensus_digest_covers_every_consensus_field_and_nothing_else (30 edits; the scheme byte and the cache rung alone move nothing), override_params_carry_the_genesis_forward_fields_and_the_digest_moves_only_when_set |
section 6 |
| Scheme 1 refused by every node until the signal | unit: a_vote_reveal_or_successor_of_another_signature_scheme_is_refused_until_a_class_names_it (RPC, in a block, a reveal, a successor; the explicit template form); fast-time: probe-scheme on three nodes |
section 6 |
| A succession carried once and refused twice | unit: a_key_hands_its_window_to_a_successor_once_and_a_second_succession_is_refused (two nodes); fast-time: infra/fast-time/key-succession.mjs (the known-failed case --expect no-succession first) |
section 6 |
| The ladder rung visible in the RPC | powEpoch.latencyLadderCache* on getBlockTemplate, the daemon line; unit: latency_ladder_rule (an inadmissible cache rung never entered; with the flag, 90 percent in seven windows enters it, N still steps after it, one rung, never back) |
section 6 |
| The codec | scheme_byte_and_succession_items_round_trip_and_an_older_decoder_stops_at_them |
section 6 |
6. Results
Filled in when the box runs are green (build and test lines, the harness summaries copied to docs/design/genesis-forward-harness/).
7. For the testnet lane
Override keys and testnet values: sig_scheme: 0, sig_scheme_activation_daa: 0, finality_succession_activation_daa: 0, latency_ladder_cache_rung: {"mib": 512, "admissible": false} (the six N rungs unchanged). Digest: with the ladder already active from genesis the cache rung's two fields enter after the six rungs' fields; the scheme switch adds two fields, the succession switch one. print_testnet_object prints the four keys.