13. Consensus and validator membership
Consensus orders blocks and produces the finalization certificates every proof in section 15 rests
on. This chapter fixes the consensus wiring that two implementations must share, the epoch structure
that paces membership change, the rules by which the validator set changes under
DynamicReshareV1, and the custody obligations that attach to the one threshold identity the chain
ever has. The consensus profile that selects between the two lifecycles is defined in section 5.
13.1 Consensus wiring
R-13.1. Consensus is Simplex with a BLS12-381 threshold VRF signing scheme under the N3f1
fault model, where f = ⌊(n − 1) / 3⌋ and quorum = n − f are derived from committee size and
never from a chainspec field.
R-13.2. Leader election uses the VRF Random elector, version V1, with rotating terms, and term
length is a consensus-critical surface (section 18).
R-13.3. A change to a stable-term RoundRobin elector activates under DynamicReshareV1 only
through a qualified consensus-critical release, and under FixedEpochZeroV1 requires a reset
(section 5).
R-13.4. track_historical_votes is true, with the library's AttributableReporter in front of
the diagnostics and evidence pipeline.
R-13.5. Retained votes are observations and never transferable proof, and group finalization certificates are the evidence for finality and for settlement.
R-13.6. There is no automatic penalty.
R-13.7. The marshal variant is standard::Deferred, and a change to the coding marshal variant
is a release coupled to the block byte limit of section 11.
R-13.8. A validator MUST NOT gossip commands to another validator.
Quorum sizes follow from the formula: n = 7 gives 5, n = 8 gives 6, and n = 10 gives 7. The spare fault at n = 7 exists only after counting every current fault. Retained votes live only until their rounds are pruned, network-reported votes are not verified before reaching a plain reporter, and threshold BLS partial signatures are not individually attributable, because enough partials forge any participant's partial, which the wrapper suppresses.
Leader terms, timeouts, and
track_historical_votes are chainspec values (section 5), and their launch values and calibration
items are listed in section 19. Gateway fan-out, the delivery floor that supports liveness, and the
meaning of an admission acknowledgement are specified in operations.
13.2 Epochs
A height is a block number from genesis. Under DynamicReshareV1 an epoch is a fixed number
of heights taken from the chainspec schedule, written blocks_per_epoch, and the boundary of an
epoch E is the final block of epoch E − 1, which is the block that carries E's EpochInfo;
epoch zero has no boundary block, and the genesis block carries its EpochInfo.
Under FixedEpochZeroV1 only epoch zero exists: the epocher is
FixedEpocher::new(NonZeroU64::MAX), and epoch zero covers heights 0..=u64::MAX - 1.
R-13.9. Under FixedEpochZeroV1, every round, proposal, vote, certificate, recovered block
context, and certified ancestry relation MUST name epoch zero and agree with that epocher
(section 11), and the engine boundary and every other caller MUST independently reject a nonzero
epoch before scheme lookup or use, before signing, and before using a certificate.
ConstantProvider is not an authorization check, because it returns its one scheme for every
requested scope.
R-13.10. A node MUST NOT derive retained_marshal_blocks, recovery horizons, or exporter
retention from the u64::MAX fixed epoch length, and the ordinary marshal, stateful, proof,
publication, and outage rules remain independently configured and enforced.
13.3 The validator schedule under DynamicReshareV1
Under DynamicReshareV1, membership is chain state. VALIDATOR_SET_SCHEDULE (section 8) is the
only command that changes it. Launch bounds are command validity, not tooling policy, and depend
only on chain state.
R-13.11. VALIDATOR_SET_SCHEDULE, authorized by the governance multisig, appends an immutable
entry {target_epoch, participants (sorted), directory}.
R-13.12. An entry is invalid unless target_epoch ≥ current_epoch + 3, and at most one entry
may be pending.
R-13.13. A pending entry is cancelable only by a pure cancel entry and only until lock-in: once
the final block of target_epoch − 2 has embedded it as next_players, a cancel of that entry is
deterministically invalid, lock-in is pinned to the state as of that block, and a cancel in that
same block is ineligible.
R-13.14. Records follow the append-only pattern: added-at and deactivated-at epochs, one-way deactivation, public keys reserved forever, and proof of possession on add.
R-13.15. An entry is valid only if all of the following hold:
a. committee size lies in [7, 64];
b. it makes at most one addition and at most one removal for its epoch;
c. it respects the fixed max_participants;
d. every added key supplies a proof of possession;
e. no carry-over is in effect (section 13.5).
R-13.16. Possession is a raw Ed25519 signature over
BLAKE3("makechain.validator-possession" || chain_id || u64(target_epoch) || identity_key),
supplied only by added identities, in identity order.
R-13.17. A participant record contains its Ed25519 identity, and never a separately invented per-validator threshold public key.
R-13.18. Nothing in validity refers to who is faulty or reachable, there is no on-chain fault set, and a liveness assessment is a governance and operator obligation before scheduling, never a consensus predicate.
R-13.19. A departing validator MUST keep dealing until a reshare excluding it is certified.
R-13.20. ParticipantsProvider::participants(epoch) reads the validator schedule as of the
state committed by the final block of epoch − 2, the same block that locks the entry and after
which a cancel of it is invalid, and never current state, because repeated calls for one epoch MUST
return byte-identical sets.
R-13.21. directory(epoch, peers) returns addresses for exactly the peers it is handed, which
the library computes as the union of the last successful output's players, the current next players,
and the newly scheduled next players, so it is correct during a carry-over.
R-13.22. Both providers are deterministic and sorted, so every honest node embeds the same
next_players.
R-13.23. Chainspec validation and startup MUST both assert two couplings the library does not check:
a. marshal retention, max_pending_acks + 1 + retained_marshal_blocks ≥ blocks_per_epoch;
b. dealer-log slot capacity,
blocks_per_epoch − (blocks_per_epoch / 2 + 1) ≥ max(dealer_quorum, previous_quorum).
INV-GOV-02. A validator-set entry is immutable except by pure cancel; at most one is pending; deactivation is one-way and public keys are reserved forever.
INV-GOV-06. A validator-set entry cannot be cancelled after the final block of
target_epoch − 2has embedded it.
INV-GOV-03. A departing validator remains dealing until a reshare excluding it is certified; membership taking effect and reshare success are distinct events.
13.4 The four sets and the effective voting set
Four sets are distinct and named.
| Set | What it is |
|---|---|
| Scheduled set | the participants of a validator-set entry in chain state |
| Ceremony players | EpochInfo.players, the participants of a reshare ceremony |
| Effective signer set | the players of the last successful reshare output, which is what the voting scheme registers |
| Transport union | the peers the directory serves, spanning current, scheduled, and departing validators together with the chainspec exporter roster (section 5) |
R-13.24. A membership change is announced when it is scheduled, in effect for the ceremony when players advance, and effective for voting only when a reshare that includes the new member succeeds.
R-13.25. The effective voting set is the effective signer set, a scheduled change MUST NOT be treated as effective for voting before its reshare succeeds, and transport membership never determines voting membership.
R-13.26. Every quorum-dependent consumer uses the effective signer set, including consumers outside consensus (see operations).
The finalized events for those three moments are distinct (section 14).
INV-GOV-05. The effective voting set is the last successful reshare's players; a scheduled change is never treated as effective for voting before that.
13.5 Reshare and carry-over
A carry-over is the continued use of the previous epoch's threshold output when a reshare
reports the library's Failure outcome.
R-13.27. Under DynamicReshareV1 a DKG reshare runs per epoch, carry-over of the previous
output is permitted only on the Failure outcome, and any other divergence is fatal-local
(section 12).
R-13.28. Every carry-over emits a finalized system event (section 14), whose emitting block is
the boundary block that carries the EpochInfo recording that Failure outcome; escalation on
consecutive carry-overs is an operator alert policy (section 19) and never an automatic halt,
removal, or trust change.
R-13.29. A stray or mismatched ceremony payload is rejected on verification (section 11), the reshare wrapper compares the reconstructed boundary outcome against the block's payload, and verification stays pending while a following node lacks the ceremony history, which is transient inability and not invalidity (section 12).
R-13.30. Under DynamicReshareV1, validators install the per-epoch scheme of an epoch E from
the EpochInfo carried by E's boundary block, the final block of E − 1, and under
FixedEpochZeroV1 the genesis scheme is the only scheme.
R-13.31. Under DynamicReshareV1, a late joiner probes the chainspec bootstrap committee until
f + 1 answer, takes the highest certified floor, and reads that epoch's boundary block.
R-13.32. A certified floor at or before the epoch midpoint lets a late joiner participate in that epoch's reshare, a later floor makes it a follower until the next boundary, and in either case it can sign ordinary blocks as soon as it holds a share.
R-13.33. A scheduled future validator MUST state-sync while it is still a next player.
R-13.34. The chainspec bootstrap list is updated by release (section 18), and an anchor supplied
by a validator or read from the store is a recent finalization plus its EpochInfo, adopted only if
its height is within max_anchor_age blocks of the highest certificate-verified finalization among
the answers of f + 1 members of the chainspec bootstrap committee, f computed from that
committee's size; a height reported without a certificate the joiner verifies is never such an
answer, and max_anchor_age is a calibration item (section 19).
The carry-over escalation thresholds are fixed in section 19. Anchor freshness is weak
subjectivity: a node started from a very old anchor trusts its operator's choice of anchor, and a
stale bootstrap list reduces the freshness bound to that same weak subjectivity, because the f + 1
answers then need not include a current honest validator and can only under-report the tip.
13.6 Threshold identity and share custody
R-13.35. The group public key established at genesis is preserved under the selected profile:
fixed for the life of a FixedEpochZeroV1 chain, and preserved by construction across every
DynamicReshareV1 reshare and membership change, which the library asserts.
R-13.36. There is no in-band identity rotation, and a new group key means a new genesis and a
new chain_id, which is a chain fork by definition (section 5).
Light clients never need private shares (section 15).
INV-GOV-04. The group public key is invariant across reshares and membership changes; a new key is a new chain.
Under DynamicReshareV1, share custody is operator-owned and outside the protocol, and the
SecretStore contract below is binding.
R-13.37. Every share, dealer seed, and received dealing MUST be durable before its write resolves, because otherwise a dealer can equivocate on restart.
R-13.38. That material MUST NOT be written to protocol storage, carried on chain, or sent to peers.
R-13.39. A share lost after a successful epoch is unrecoverable by the protocol, and the validator waits to be re-dealt as a future player.
R-13.40. A share revealed by dealers because the player was offline is public and MUST be treated as public.
R-13.41. Operators MUST securely erase material that is no longer required by the effective output or by the bounded recovery path, only after the handoff to the successor share is durable, and MUST keep no long-lived backups of obsolete material.
R-13.42. During a carry-over the previous sharing remains effective and MUST be kept.
The invariant group key carries a lifetime assumption. A quorum of shares from any single past epoch can sign under the group identity forever, because a certificate verifier checks identity and namespace, not the current committee. Membership removal cannot revoke that, and a suspected historical quorum compromise is an incident whose only remedy is a new chain. A client's recent checkpoint bounds the history it will accept and does not prevent an old quorum from forging a fake successor, which is the accepted trust model at launch (section 4).
The library recovers a preceding share before it prunes, and its prune hook only forgets entries, so
zeroization and media erasure are the operator's SecretStore implementation.
13.7 What each profile changes
Section 5 defines each profile, including its genesis threshold source and epoch construction, and section 18 states which release operations each profile admits; this table lists only the consensus consequences.
| Aspect | FixedEpochZeroV1 |
DynamicReshareV1 |
|---|---|---|
| Committee | exactly seven fixed validators, ordered by the chainspec | 7 at genesis, bounds [7, 64] |
VALIDATOR_SET_SCHEDULE |
every variant deterministically invalid | governs membership per section 13.3 |
| Reshare and carry-over | none | per epoch, with carry-over only on the Failure outcome |
| DKG block payloads | all None in every block (section 11) |
used under the reshare rules of section 13.5 |
| Per-epoch scheme | genesis scheme only | installed from each boundary block's EpochInfo |
| Committee, directory, output, or group-key change | reset (section 5) | schedule entry plus reshare |
R-13.43. Under FixedEpochZeroV1, the chainspec's ordered seven-member committee and directory
are the only scheduled set, ceremony set, and effective signing set, every VALIDATOR_SET_SCHEDULE
variant is deterministically invalid for every payload and mutates no state, and no pending
validator entry can exist in reachable state. Under every profile the transport set is that
committee together with the chainspec exporter roster (section 5), which carries no scheduled,
ceremony, or signing membership and never counts toward a quorum.
R-13.44. Retired; its content is R-5.30.
R-13.45. Under FixedEpochZeroV1, restart and rebootstrap MUST recover the identical fixed
committee, public output, genesis binding, and local share identity.
13.8 Rationale
This subsection is informative.
Rotating leader terms and the VRF elector are the selected baseline. A stable term would not force
an offline leader to stall the chain for a whole term, because participants vote to nullify the
remaining views after a local stall timeout and a quorum certificate skips to the next term, but
Random cannot use a term length greater than one and term length is consensus-critical, so stable
terms remain a separate idea. Historical votes are tracked so misbehaviour is attributable.
Membership under DynamicReshareV1 is governed and dynamic because a fixed set makes every operator
change a new chain. The three-epoch lead follows the participant lock-in the DKG primitive requires,
one change per epoch keeps every reshare bounded, and seven is the smallest committee with a spare
fault during a one-member replacement. The group key's invariance is what makes a single trust
anchor sufficient for light clients.