diff --git a/aips/AIP-22.md b/aips/AIP-22.md index 1b0d49b..8eefde2 100644 --- a/aips/AIP-22.md +++ b/aips/AIP-22.md @@ -152,7 +152,7 @@ From `H_fin` (a height at or above `H_id`), on every node that validates or read 1. **The certificate chain is required for canonicality.** A chain is canonical only if every anchor height at or above `H_fin` carries a certificate satisfying section 1 for every scheme the schedule requires there. A missing or short certificate at an anchor height is an invalid block, as today; in addition, a node MUST NOT extend its canonical head more than `I_F` blocks past the last anchor it has verified, so a fork that cannot produce certificates cannot be followed beyond one Falcon interval. 2. **ECDSA commit seals are not a validity criterion.** The rule that a header carries at least Q ECDSA committed seals of the parent's set is no longer applied to headers at or above `H_fin`; the element MAY be present and MAY be empty. Between anchors a header is admitted on the basis of the hash chain, the proposer check and, until section 5, the ECDSA seals as an anti-spam admission filter whose failure is a refusal but whose success proves nothing about finality. The finality of the block is established only by the covering anchor (section 1). -3. **The ECDSA signature of a consensus message is not a validity criterion.** The message counts as a vote iff its post-quantum seal(s) verify and bind to an `id` in the set (section 3.3). The envelope is kept because the upstream wire format carries it and because the second client's decoder expects it; a validator MAY sign it with any secp256k1 key. +3. **The ECDSA signature of a consensus message is not a validity criterion.** The message counts as a vote iff its post-quantum seal(s) verify and bind to an `id` in the set (section 3.3). The envelope is kept because the upstream wire format carries it and because the second client's decoder expects it; a validator MAY sign it with any secp256k1 key. Precisely: a proposal, prepare or round-change has an author at all only once its seal verifies under the key of its index's row over the message of its own domain (a round-change in the form its prepared claim names), because the author also decides whether a message is relayed, and the relay happens before the vote is judged; a commit's author is its index's `id` only when that `id` is the commit seal signer's, and its seal is judged by the commit layer. A node refuses to start with `H_fin` set if any of the four seal layers is not armed at or below `H_fin` (`AERE-PQC-FIN-02`), and a validator refuses if it would not seal all four messages from `H_fin` (`AERE-PQC-FIN-03`): an unsealed message has no author, so such a validator would be silent from `H_fin` while looking healthy. 4. **Provisional tail and rollback.** A block imported between anchors is *provisional* until its covering anchor is imported. A node that receives a valid anchor whose parent chain differs from its provisional tail MUST reorganize to the anchored chain; the depth of such a reorganization is at most `I_F` (32) blocks by rule 1. This is new behaviour for a QBFT client, whose head has been final on import until now, and it is a testnet gate (section 9, Stage 4) with the explicit negative control: a reader node fed a classical-only fork of up to 31 blocks rolls back to the honest anchor within one interval, in both clients. 5. **Weak subjectivity, named.** A node that syncs from genesis with no other knowledge can be served, by an eclipsing peer, a fork that ends before the first enforced anchor (block 13,034,000, where the certificate schedule first required a seal); such a fork can never carry a later certificate and so can never reach the height of the honest chain, but an eclipsed node does not know that height. A syncing node therefore SHOULD be given a recent anchor (height and hash) out of band, from the published registry epoch or from the operator, and MUST refuse to consider a chain canonical that does not reach it with valid certificates. This is the same requirement every finality-gadget chain has, and it is the honest replacement for the statement "history below H is protected by ECDSA only": with the checkpoint, a rewrite at any height, including below 13,014,000, would have to reproduce every certificate above it, and the strongest of those requires Q post-quantum keys. 6. **Fallback and crypto-agility.** The schemes required at an anchor are named by the scheme-interval schedule, by height; the keys are bound by the registry epoch, by height; the on-chain scheme registry (`CryptoRegistry`, AIP-4 and AIP-7) names what verifiers exist. Retiring a scheme, adding one (ML-DSA-65 keys would be a founder ceremony: new keys), or changing an interval is a schedule step at a future height on every node that judges headers, the procedure the fleet has executed for the base-fee floor, the seal threshold, the hybrid, the interval and the four message layers. Because the hybrid rule is a conjunction, a scheme that is *broken* does not weaken the certificate (the adversary still needs the other); a scheme whose *implementation* stops producing seals halts anchors, and the answer is a uniform schedule step or the existing uniform emergency options, never a single node's setting. @@ -249,6 +249,10 @@ Activation heights on chain 2800 are the founder's (AIP-19 acceptance per stage) **2026-09-23 16:37Z, stage 3b proven on testnet 28001: a validator's keys rotated under its id at B2 = 3,097,600.** From H_k = 3,080,010 (the bindHeight of the switch epoch) the keys that seal are read from the v3 epoch in force, in both clients and in the public verifier; a startup guard in each client refuses to start unless the epoch at H_k carries exactly the keys of the existing registries (10 keys of 5 rows, verified on all six nodes), so the switch changed no key. A node's own signing key and index are derived the same way: a validator holds its current key and its NEXT keys as files, and from each height it signs with the key its row carries in the epoch in force there, at that row's index; a second startup guard refuses to start a validator registered in an epoch from H_k whose key it does not hold, because it would otherwise go silent at that epoch with every configuration check green. The rotation: a new PROBA key pair (Falcon-512 and SLH-DSA-SHA2-128s) for validator 2 was generated on its own host by the registry tool (owner-only files, nothing private printed); its row in the epoch bound at B2 keeps the id `0xe7e6b4d3...`, carries the new keys with their proofs of possession, and carries a rotation signed by each previous key over the new row hash; the other four rows keep their keys, with possession re-proven for the new bindHeight. The epoch's registry hash `0xb5847f46...` is identical in the Besu-derived strict loader and in the independent JavaScript implementation, which also refuses the same epoch without its predecessor (REG3-09, the rotated row then reads as a new id) and with the rotation signature corrupted (REG3-12). All six nodes were armed before B2 (five Besu-derived on a distribution that loses no class of the live one, the Nethermind-derived validator on a binary built on its host), validator 2 with its next keys; the epoch and the `keysFrom` mark were published in the testnet's manifest index the same hour. Over [B2 - 1, B2 + 500]: the validator set was the same five ids at B2 - 1, B2 and B2 + 500; both clients held identical hashes for all 502 blocks; the rhythm was 0.565 s per block before and 0.568 after; the public verifier run as a stranger (no variables) found the epoch and the mark in the published index and accepted 15 of 15 anchors; and the NEGATIVE CONTROL, the same verifier on a copy of the published manifests without the epoch bound at B2, refused validator 2's Falcon seal as invalid on 12 of the 15 anchors (the other three did not carry that validator's seal), so the seals after B2 are made with the rotated key and nothing else verifies them. Not measured, and stated: the behaviour of a node left without the epoch at B2 (the testnet has none), and a rotation on chain 2800, which means new keys and a founder decision. +**2026-09-23 18:47-19:00Z, stage 4 (message authorship from verified seals) armed on testnet 28001 for H_fin = 3,117,000, and two holes found by reading the code before it.** Reading both clients before any live step showed that "the author is the id of the seal's index" is only as good as the check that the seal verifies, and that the check was not where the author is decided: (a) the Nethermind-derived client verified the post-quantum seals of commits only; it counted a prepare, a proposal's implicit prepare and a round-change by their author alone, so an author read from an unverified index would have let anyone who can reach that node vote in any validator's name; (b) the Besu-derived client relays a message as soon as its author is in the set, before its seal layers judge it, so the same author would have let anyone make every validator relay messages in any validator's name, where until H_fin that required a validator's ECDSA key; and (c) in the Besu-derived client a seal layer whose property is unset verifies nothing. The form armed, in both clients: from H_fin a proposal, prepare or round-change has an author only once its Falcon-512 seal verifies under the key of its index's row in the v3 epoch in force, over the message of its own domain (a round-change in the form its prepared claim names), through the same verifier and message builder as the seal layers; a commit's author is its index's id only when that id is the commit seal signer's, and its seal is judged by the commit layer; a node refuses to start with H_fin set if a seal layer is not armed at or below it (`AERE-PQC-FIN-02`) and a validator refuses if it would not seal all four messages from H_fin (`AERE-PQC-FIN-03`, a node that would otherwise be silent at H_fin while looking healthy); `AERE-PQC-FIN-01` requires the v3 keys at or below H_fin and the chain id. Probes: in the Besu-derived client the author with an injected verifier, the real payloads with real Falcon-512 keys and a foreign envelope key, and both guards (three modules 1,137 tests, 0 failures, with the application compiled; negative controls planted at run time "H_fin never active", "every seal verifies", "a commit binds to any signer", "FIN-02 never refuses", "FIN-03 never refuses", each turning its probes red); in the Nethermind-derived client the same cases on its wire codec with probe Falcon keys (34 tests, 35 with the shared vectors below; six negative controls red with the tests run). Shared vectors: seven real messages (prepare, commit, round-change) written by the Besu-derived client with a foreign envelope and real seals, each expected author asserted through its own decoding path, decode to the same author in the Nethermind-derived client, seven of seven, and the "every seal verifies" and "a commit binds to any signer" controls turn that probe red. Armed: the five Besu-derived nodes on a distribution built from the fleet laboratory that loses no class of the live one (six classes added), one at a time with cooling, each logging "H_fin armed at 3117000"; the Nethermind-derived validator on a binary built on its host that loses no module of the live one. Also in this stage and deployed with it, inert unless configured: the checkpoint option (`aere.pq.checkpoint` / `AERE_PQ_CHECKPOINT`, a header rule that refuses a header at a checkpoint height with another hash; not configured on the testnet) and, in the Nethermind-derived client, the sync-mode guard the Besu-derived client has had since 2026-08-02 (with the anchor armed, fast or snap sync refuses to start, proven at run time). Not in this stage on the testnet, and stated: rule 2 of section 4 (see the open question in the plan: whether ECDSA commit seals may be empty between anchors), a head rule for validators fed a classical-only fork (it needs a measurement with N >= 4), and the Nethermind-derived reader under attack (not measured). + +**2026-09-23 19:40Z, stage 4 proven on testnet 28001 (H_fin = 3,117,000).** Over [H_fin - 1, H_fin + 500]: H_fin armed in the environment of all six nodes (five of five Besu-derived, one of one Nethermind-derived; positive control on the same read, the H_k property, five of five and one of one); both clients held identical hashes for all 502 blocks; all five ids proposed (101, 100, 100, 100, 100 blocks), so every validator's proposals and votes, the Nethermind-derived validator's included, have an author in the other client; the rhythm was 0.568 s per block before and 0.566 after (ratio 0.996); at the crossing itself the 60 blocks after H_fin took 33 s against 35 s for the 59 before. NEGATIVE CONTROL OF THE METHOD: the same measures on the known-bad window [3,080,010, 3,080,510] (the second defect at H_id, four proposers) come out red (four proposers, ratio 2.741), and the proof refuses to speak (NOT MEASURED) unless H_fin is armed on all six nodes, which it would otherwise have reported as proven on a window without the stage. Not measured, and stated: a forged message on the network (no testnet node can inject one; its refusal is proven by the probes and negative controls in both clients and by the shared vectors). + **2026-09-23, stage 3 foundation: the registry v3 forms, pinned.** Section 3.2 names a "canonical serialization"; the bytes are now fixed, identically, in the Besu-derived client (`PqRegistryV3`), the Nethermind-derived client (`PqRegistryV3`) and a third, independent JavaScript implementation, and pinned by 19 shared conformance vectors (two valid epochs, the second rotating one validator's keys under its id; seventeen refusals, each differing from a valid manifest in one field with the signatures re-made over that field, one per refusal code `AERE-PQC-REG3-01` to `-16`): ```