aere-research/aips/AIP-22.md

281 lines
62 KiB
Markdown

# AIP-22: Post-Quantum-Only Finality (Chained Quorum Certificates, PQ Validator Identity, ECDSA Demoted to Compatibility)
## Preamble
| Field | Value |
| --- | --- |
| AIP | 22 |
| Title | Post-Quantum-Only Finality (Chained Quorum Certificates, PQ Validator Identity, ECDSA Demoted to Compatibility) |
| Author | Aere Network Foundation |
| Type | Standards Track |
| Category | Core |
| Status | Draft (design only; nothing described here is implemented or deployed at the Created date) |
| Created | 2026-09-23 |
| Requires | AIP-15 (anchor certificate under the block hash), AIP-21 (per-block seal record), the per-message post-quantum enforcement of SPEC section 2.6 (in force on chain 2800 since block 17,700,000), AIP-19 (the acceptance process) |
| Supersedes | None |
| Superseded-By | None |
| Ratification | Not ratified; testnet-gated (chain 28001 first, both clients), second-client-gated (both clients accept and refuse the same headers), founder-gated per activation height (AIP-19), external-audit gated before the canonical rule (section 4 of the Specification) is armed on chain 2800 (the gate AIP-15 already names) |
## Abstract
This AIP makes the finality of every block of an Aere QBFT chain a post-quantum property: a node that syncs from genesis establishes, for every block, that a quorum of validators sealed it with post-quantum signatures, without relying on any secp256k1 ECDSA signature. It does so with four changes, each activated by height and each a coordinated fork: (1) the anchor certificate's per-scheme seal count is raised from the current six of ten to Q, the QBFT quorum (seven of ten); (2) a Falcon-512 certificate is carried every 32nd block and the SLH-DSA-SHA2-128s certificate every 128th, so every block is covered, through the parent-hash chain, within 32 blocks by Q Falcon-512 seals (a single lattice assumption) and within 128 blocks also by Q SLH-DSA seals (a second, hash-based assumption); (3) validator identity becomes an address derived from the validator's post-quantum key, bound to all its keys by a versioned registry, so the validator set, the proposer rotation and message authorship no longer name an ECDSA key; (4) ECDSA commit seals and message signatures stop being validity criteria and remain only as a compatibility envelope on the wire, then leave the header. The design rejects a per-block certificate (259 to 370 GB per node per year at ten validators, measured cost basis), a per-block hash-based certificate (3.07 TB per year and seconds of signing per block, measured), STARK aggregation as the finality proof (the Falcon arithmetization is not measured; the proof is a history-compression layer, not a finality proof) and threshold Falcon (no standard, no audited implementation). Every quantity below carries its provenance; what is not measured is marked.
## Motivation
Chain 2800 today has the following measured shape (sources in the Specification): every QBFT message on the hot path (proposal, prepare, commit, round-change) is refused unless it carries a valid Falcon-512 seal bound to its author, since block 17,700,000 on both clients; every 128th block carries a hybrid Falcon-512 + SLH-DSA-SHA2-128s certificate over its parent under the block hash, with at least six valid seals per scheme of the ten validators; and `aere_getPqFinality` returns, on every validator, the Falcon seals it heard for a recent block, re-verified (ten of ten on 2026-09-22). A validator that is online therefore finalizes nothing without post-quantum seals.
What remains classical is exactly what a node that is NOT online, one that syncs the history, is asked to believe:
1. Between two anchors, the header of a block carries only ECDSA commit seals. A syncing node checks those and nothing post-quantum for up to 127 blocks (about 72 seconds at the measured block period). Its post-quantum assurance for those blocks arrives only with the next anchor, and the anchor's certificate is bound to the parent hash, so the assurance is real but delayed, and it is not stated as a rule anywhere.
2. The certificate threshold is six of ten (schedule step `14961456:6`), which is f + 3 at N = 10 and below the QBFT quorum of seven (recorded in the repository's findings register on 2026-09-11). A hostile reader is right to say: the certificate proves that six validators signed, it does not prove that consensus was reached.
3. Validator identity is an ECDSA address: the validator list in `extraData`, the proposer rotation, the `coinbase`, the vote recipient, the registry's `claim`, and the peer-forwarding predicate of the Besu-derived client (it forwards Istanbul messages only to peers whose node-key address is in the set, measured 2026-09-02) all name secp256k1 keys. The Falcon registry binds an index to that address; the address itself is classical.
4. The ECDSA signature of every consensus message and the ECDSA commit seals in every header are still validity criteria. The live security is therefore "ECDSA AND Falcon" for an online node, which is stronger than either alone, and "ECDSA, then Falcon at the next anchor" for a syncing one.
The Foundation's stated goal, restated on 2026-09-22, is a consensus whose safety and finality do not depend on ECDSA at all. This AIP is the path from the measured state above to that goal, with the cost of each step in bytes, disk, bandwidth and latency, and with the founder decisions it needs named.
## Specification
Notation: `N` the validator set size at a height, `f = floor((N - 1) / 3)`, `Q = ceil(2N / 3)` the QBFT commit quorum (Besu `BftHelpers.calculateRequiredValidatorQuorum`; 7 at N = 10, 14 at N = 21). `hash(B)` is the on-chain block hash (round forced to zero, commit seals and certificate excluded, SPEC section 2.3). `M(B) = keccak256(RLP["AERE-PQ-COMMIT-1", chainId, number(B), hash(B)])` is the commit-seal message already in force (SPEC section 3.3). Scheme wire identifiers are those of the v2 certificate: 1 = Falcon-512, 2 = SLH-DSA-SHA2-128s.
Measured constants used throughout, with source:
| Quantity | Value | Provenance |
| --- | --- | --- |
| block period | 0.565 s (band 0.56 to 0.63 s across September) | **measured** 2026-08-24 over 2,000 blocks (`strategie/agregare-pq/CIFRE-MASURATE.md` #4); band from the operating notes of 2026-09-11 and 2026-09-22 |
| blocks per year at 0.565 s | 55,854,159 | derived from the above (`STUDIU-2026-08-24.md`, section 2 conventions) |
| blocks per day | ~152,900 | derived (CIFRE #4) |
| Falcon-512 seal in extraData, with index and RLP wrapper | 663 B (field 648 to 660 B, mean 655.1) | **measured** on 178 seals of 20 live anchors (CIFRE #1) |
| SLH-DSA-SHA2-128s signature | 7,856 B (+5 B wrapper, **estimate**) | **spec** FIPS 205 (CIFRE #2) |
| ECDSA commit seal | 65 B | **measured** (CIFRE #8) |
| live hybrid anchor certificate at nine Falcon + nine SLH-DSA seals | 77,386 B | **measured** 2026-09-05 on anchor 17,184,144 (operating notes) |
| ordinary header at N = 9 | 1,259 B (extraData 634 B) | **measured** 2026-09-04 on block 17,047,599 (`consensus-pqc/COSTUL-DISCULUI-HIBRID-2026-09-04.md`); 634 B from CIFRE #1 |
| total database growth per validator | 58 to 71 GB per year, headers about 95% of disk | **measured** on the fleet in August 2026 as size divided by height (operating notes; `STUDIU-2026-08-24.md` section 1) |
| Falcon-512 verify | 0.068 ms median (Bouncy Castle, validator-class host); 0.092 ms median, 0.215 ms p95 (rehearsal host) | **measured** (`audit-package-pq-consensus/evidence/EVIDENCE-INDEX.md`, `falconbench-bouncycastle.txt`; `consensus-pqc/ancora-v2/README.md`) |
| Falcon-512 sign | 0.60 ms median | **measured** (same evidence index) |
| SLH-DSA-SHA2-128s sign on the consensus thread | 0.3 to 0.7 s per signature with the JDK digest (1.4 to 2.7 s before) | **measured** 2026-09-04 on chain 2800 (findings register) |
| SLH-DSA-SHA2-128s verify | 0.669 ms median, 1.494 ms p95 on a 2-vCPU reader host with the fleet's jars (Falcon-512 on the same host and path: 0.367 ms median, ratio 1.8); 0.966 ms median on a laptop (ratio 2.9); the gas proxy (350,000 against 40,000, ratio 8.75) is not a time proxy on the consensus path | **measured** 2026-09-22 (`consensus-pqc/aip22-treapta0/REZULTATE.md`, `SealSchemeBench.java` on `SealSchemes`, negative control 30 of 30 refused) |
| from-genesis import rate with ten peers | ~1,300 blocks per second | **measured** 2026-09-11 (findings register) |
| anchor cycle on the live chain (anchor parent + anchor) | 1.33 to 2.00 s against 0.565 s for an ordinary block | **measured** 2026-09-04 and 2026-09-05 (findings register) |
| FRI proof, production parameters (log_blowup 1, 100 queries, 2^20 rows) | 639,032 B content, 655,044 B on disk; generation 6.1 to 7.1 s single thread on a laptop; verification 80 to 91 ms | **measured** 2026-08-25 (`strategie/agregare-pq/DOVADA-FRI-MASURATA-2026-08-24.md`); the FRI component only, not a Falcon arithmetization |
### 1. The target, stated exactly
**Definition (post-quantum finality of a block).** From the activation height `H_fin` (section 4), a block `B` of the canonical chain is *post-quantum final* when there exists a canonical anchor block `A` with `number(A) > number(B)` whose certificate carries, for every scheme the scheme-interval schedule requires at `number(A)`, at least `Q(number(A) - 1)` valid seals of distinct validator indexes over `M(parent(A))`, verified against the registry epoch bound at `number(A) - 1`, and the parent-hash chain from `parent(A)` down to `B` is valid. The finality is *single-assumption* when the covering anchor carries only the Falcon-512 certificate and *dual-assumption* when it also carries the SLH-DSA-SHA2-128s certificate.
**What the definition rests on.** Two cryptographic assumptions and nothing else: the unforgeability of the scheme(s) in the covering certificate, and the collision resistance of keccak-256, which is what makes `hash(parent(A))` a commitment to every ancestor. Keccak-256 is a hash function; the relevant quantum collision attack (Brassard, Hoyer, Tapp) is about 2^85 operations with impractical memory, and Grover's preimage attack leaves about 2^128, so 256-bit output holds under current public knowledge (`STUDIU-2026-08-24.md`, section 6). No ECDSA signature appears in the definition.
**Bounded delay.** With the Falcon certificate every `I_F = 32` blocks and the SLH-DSA certificate every `I_S = 128` blocks (section 2), every block becomes single-assumption post-quantum final within at most 32 blocks (about 18.1 s at 0.565 s, **estimate** from the measured period) and dual-assumption final within at most 128 blocks (about 72.3 s). Before that, an online validator already holds at least Q Falcon commit seals for the block, because a commit has counted only with a valid seal since block 17,250,000 (AIP-21 exposes them, re-verified, through `aere_getPqFinality`); that record is the zero-delay, verifiable, non-canonical proof, and the anchor is the canonical, permanent one. The two are the same claim at two ages, exactly as AIP-21 states for the anchor today.
**The transition principle.** ECDSA is not removed from the wire in one step. It is first demoted from a validity criterion to a compatibility envelope (section 4), then removed from the header (section 5). At no activation height does a node accept a block on ECDSA alone that it would have refused before; every step only tightens what a syncing node requires.
### 2. Per-block proof: the options, with cost, and the choice
Costs are the post-quantum overhead per block, over the ECDSA baseline that remains until section 5, as in the sizing study. "All N" attaches every heard seal (K is a floor: measured 8 to 9 of 9 attached at N = 9, cap 9 today per the findings register of 2026-09-11); "Q only" caps the proposer at the quorum.
| Option | Bytes per block, N = 10 (Q = 7) | GB per node per year, N = 10 | N = 21 (Q = 14) | Bandwidth per event, serial to N-1 peers at 100 Mbps (hypothesis of the study, not measured) | Latency | Verdict |
| --- | --- | --- | --- | --- | --- | --- |
| (1) Falcon quorum certificate in every header (over the parent) | 4,641 (Q only) to 6,630 (all 10) | 259.2 to 370.3 | 9,282 to 13,923 B per block, 518.4 to 777.7 GB | 4.8 ms at N = 10, 22.3 ms at N = 21: holds | 7 verifies 0.5 ms, sign 0.6 ms: negligible | **rejected on disk**: 4 to 6 times the whole measured growth of a node today; AIP-21 rejected the same family on 2026-09-17 at 247 GB to 2.9 TB |
| (2) SLH-DSA quorum certificate in every header | 55,027 (Q only) to 78,610 (all 10) | 3,073 to 4,391 | 110,054 to 165,081 B per block | 56.6 ms at N = 10 for 78,610 B: just above the rule; 264 ms at N = 21: violated | signing 0.3 to 0.7 s per validator per block on the consensus thread (measured); per-commit SLH-DSA took testnet 28001 to 4 s per block on 2026-09-03 (testnet record) | **rejected twice**: disk and block period |
| (3a) Per-block certificate written k blocks later | same bytes as (1) | same | same | same | delay k buys the proposer gathering time, nothing else | **rejected**: the delay does not reduce the bytes |
| (3b) **Chained quorum certificate on a grid** (this AIP): anchor `A` carries the certificate over `parent(A)`; the parent-hash chain extends it to every block below | Falcon all 10 at I_F = 32: 207.2; SLH-DSA all 10 at I_S = 128: 614.1; total 821.3 | 11.6 + 34.3 = **45.9** (Q only: 8.1 + 24.0 = 32.1) | Falcon all 21 at 32: 435.1 B, 24.3 GB; SLH-DSA all 21 at 128: 1,289.7 B, 72.0 GB; total 96.3 GB | Falcon anchor 6,630 B: 4.8 ms (N = 10), 22.3 ms (N = 21): holds; hybrid anchor 85,240 B at N = 10: 61.4 ms, just above the 56.5 ms rule (the live anchor cycle, 1.33 to 2.00 s measured, is the binding constraint today, not propagation); at N = 21 the SLH-DSA anchor (165 KB) takes 264 ms: violated at 100 Mbps, 26.4 ms at 1 Gbps | Falcon: negligible; SLH-DSA anchor cycle 1.33 to 2.00 s measured today, unchanged by this AIP | **chosen**; delta against today's live cost (ten plus ten seals at 128: 37.2 GB) is +8.7 GB per node per year |
| (4) STARK proof per epoch that "Q seals verify" | 639,032 B (FRI component only, full proof larger and [NOT MEASURED]) per epoch: 4,992 B per block at epoch 128, 624 B at 1,024, 4.2 B at one proof per day | 278.8 at epoch 128; 34.9 at 1,024; 0.23 at one per day | constant in N (that is its only advantage) | 460 ms at N = 10, 1,022 ms at N = 21: violated at every N if the proof is in the block's critical path | generation 6.1 to 7.1 s single thread for the FRI component; the Falcon arithmetization and its trace height are [NOT MEASURED]; if the trace needs 2^22 rows, 21 to 27 s (extrapolation) | **not the finality proof**: it is asynchronous by necessity, it is larger than a Falcon certificate at any epoch under 108 anchors, and its cost is unmeasured where it matters; it is the history-compression layer of section 7 |
| (5) Threshold or aggregate Falcon | not applicable | | | | | **excluded**: no NIST standard aggregates Falcon (FIPS 206 draft standardizes single signatures), Falcon signatures are Gaussian samples on an NTRU lattice and do not add across keys, and no audited implementation exists; the chain's crypto-agility (section 4) lets it be adopted if that changes |
Derivations, so they can be checked: GB per year = bytes per block x 55,854,159 / 10^9. Falcon all 10 = 10 x 663 = 6,630 B; Q only = 7 x 663 = 4,641 B. SLH-DSA all 10 = 10 x 7,861 = 78,610 B; Q only = 55,027 B. Live today: 9 + 9 seals at 128 = 77,386 B measured = 604.6 B per block = 33.8 GB; ten plus ten = 85,240 B = 666 B per block = 37.2 GB. Serial propagation time = bytes x 8 x (N - 1) / 10^8 s; the 56.5 ms rule is 10% of the block period, the sizing study's house rule. The bandwidth between validators has never been measured; the 100 Mbps figure is the study's named hypothesis.
**Why (3b) and why these intervals.** The anchor already is a chained quorum proof in everything but threshold and statement: its certificate signs `M(parent(A))`, and `hash(parent(A))` commits to every earlier block. Making it the finality criterion costs no new format (the v2 certificate is already scheme-tagged) and no new gathering mechanism (the proposer of `A` finalized `parent(A)` by hearing at least Q sealed commits, since block 17,250,000). `I_F = 32` is the grid the chain ran on from 13,014,000 to 17,225,968 at N = 9 without a rate change (measured 0.565 s across that period), so its cost and liveness are not an estimate; `I_S = 128` is the grid in force today and keeps the SLH-DSA cost at its measured level. The Falcon-only intermediate anchors add 3 x 6,630 / 128 = 155.4 B per block, that is +8.7 GB per node per year, and bring single-assumption finality from 72 s to 18 s. A smaller `I_F` (16: +23.1 GB total Falcon, 9 s; 8: +46.3 GB, 4.5 s) is a founder choice with the table above; this AIP recommends 32.
**Certificate at a Falcon-only anchor.** The v2 encoding, unchanged: `RLP[2, [[1, index, signature], ...]]` under domain `AERE-PQ-ANCHOR-2`, digest in `vanityData` as today. A scheme-interval schedule (section 2.1) states which schemes are required at which anchor heights; the minimum applies per scheme, as today. A certificate that carries an SLH-DSA row at a height where the schedule does not require it is still verified and still counted for that scheme, exactly as any surplus seal is today; a certificate that lacks a required scheme's quorum is invalid.
#### 2.1 Parameters
| Property (Besu-derived; Nethermind-derived twin in parentheses) | Meaning | Value proposed for chain 2800 |
| --- | --- | --- |
| `aere.pq.anchorMinSeals` step (`AERE_PQ_ANCHOR_MIN_SEALS`) | per-scheme minimum, floor semantics, existing property | `<H_K>:7` appended; at N = 21 a further step `:14` in the epoch that admits the eleventh validator |
| `aere.pq.anchor.maxSeals` (`AERE_PQ_ANCHOR_MAX_SEALS`) | proposer cap, existing | 10 (raised from today's 9), then N |
| `aere.pq.schemeIntervalSchedule` (`AERE_PQ_SCHEME_INTERVAL_SCHEDULE`), **new** | from height `h`, scheme `s` is required at anchor heights `a` with `(a - H_grid) mod I_s == 0`; `I_s` MUST be a multiple of the base anchor interval; the anchor grid itself becomes the finest interval named | `<H_I>:falcon-512/32+slh-dsa-sha2-128s/128` |
| public verifier `SCHEME_INTERVAL_SCHEDULE` | the same rule in `tools/verify-anchor.mjs` | same |
The threshold guard (`PqAnchorThresholdGuard`, doctrine of 2026-08-20) refuses a step above N - f and warns at or above Q; at N = 10, Q = N - f = 7, so K = 7 is configurable today and starts under the guard's "liveness tax" warning. The warning's arithmetic is the honest statement of the cost: with f = 3 validators down, an anchor needs every one of the remaining seven seals. QBFT itself needs those same seven to commit any block in that state, so the new condition is not a new fault tolerance, it is a timing condition on the anchor's proposer, bounded by a round change (section 8, Liveness).
### 3. Validator identity from the post-quantum key
#### 3.1 The identifier
From the epoch activation height `H_id`, a validator is identified by
```
id = keccak256(0x50 || 0x20 || falcon512_public_key)[12:32]
```
where `falcon512_public_key` is the standard 897-byte encoding (header byte `0x09` followed by the 896-byte `h` polynomial; the registries of today store the 896-byte polynomial without the header, SPEC section 3.5, and implementations MUST prepend it before hashing). This is exactly the sender derivation of AIP-20 with the identifier that AIP-20 reserves for Falcon-512 (`alg_id 0x20`), so that when a later AIP activates Falcon-512 transactions, the validator's `coinbase` proceeds are spendable by the validator's own consensus key with no classical key. Until such an AIP, the `id` receives whatever the fee rules route to `coinbase` and cannot spend it; this is stated, not hidden.
The `id` is assigned at the validator's first registration under registry v3 and is retained through key rotations (section 3.2): it is a label after that, not a live derivation. A hostile reader should note the consequence: if Falcon-512 falls, the label's *account* is as exposed as any Falcon account; the *consensus* is not, because the hybrid rule requires the SLH-DSA seal of the same index and the registry binds both keys to the id.
#### 3.2 Registry v3
A registry epoch is a JSON manifest with rows keyed `"0".."count-1"`, `formatVersion 3`, `chainId`, `bindHeight`, `count`, and per row:
| Field | Content | Status |
| --- | --- | --- |
| `id` | 20 bytes, section 3.1 | required |
| `keys` | map scheme name to public key: `falcon-512` (897 B standard encoding), `slh-dsa-sha2-128s` (32 B); further schemes as the scheme registry admits them | required, at least the schemes the scheme-interval schedule names |
| `pop` | map scheme name to a possession proof by that key over `"AERE-PQ-POP-3" || uint8(3) || uint64be(chainId) || uint64be(bindHeight) || uint32be(count) || uint32be(index) || id(20) || uint8(schemeWireId) || uint32be(len(pk)) || pk` | required per key (today only the Falcon key carries a proof of possession; the SLH-DSA key gets one) |
| `rotation` | when a row changes any key against the previous epoch's row of the same `id`: a signature by EACH of the previous epoch's keys of that id over the new row hash, domain `AERE-PQ-ROTATE-3` | required on a changed row, absent otherwise |
| `ecdsa` | `{ addr, claim }`: the validator's classical address and an EIP-191 claim by that key over the row hash in the `AERE-PQ-CLAIM-1` context of today | required in the epoch that performs the switch at `H_id` (it is the verifiable link from the old set to the new one), optional afterwards |
| `transport` | `{ secp256k1Address }`: the address of the node key the validator's client uses on RLPx | optional; see 3.4 |
Row hash: `keccak256("AERE-PQ-ROW-3" || canonical serialization of the row without `rotation` and `ecdsa`)`. Registry hash: the root of a keccak-256 binary Merkle tree over the row hashes in index order (odd node duplicated), domain-separated as `keccak256("AERE-PQ-REGISTRY-3" || uint64be(chainId) || uint64be(bindHeight) || uint32be(count) || root)`. The Merkle form exists so that a row's membership can be proven on chain with `ceil(log2(count))` hashes (section 6); the v1 and v2 pre-images are flat and cost the whole registry as calldata. The v3 hash is pinned exactly as today: slot 0 of a new immutable anchor contract for a live chain (AIP-15, exercised on chain 2800 at 13,889,290, 13,600,000 and 18,082,816), or `pqRegistryHash` in the genesis config for a new chain (SPEC section 1.5). The domain change guarantees that no v1 or v2 file can satisfy a v3 schedule entry.
Loading is strict, as today: `count` present, indices exactly `0..count-1`, every required scheme present with a verifying `pop`, every changed row carrying verifying `rotation` signatures by all previous keys, an ambiguous file refuses rather than hashes.
#### 3.3 What changes in QBFT
| Where | Today | From `H_id` |
| --- | --- | --- |
| `extraData` element 1, the validator list | 20-byte ECDSA addresses | 20-byte `id`s; at exactly `H_id` the list is the registry's `id`s in the order of the registry index of each validator of the set at `H_id - 1`, obtained through each row's `ecdsa.addr`; a set member without a v3 row at `H_id` is a configuration fault that refuses startup on every node, never a silent drop |
| `extraData` element 2, the vote | recipient is an ECDSA address | recipient is an `id`; a vote for an `id` with no row in the head registry is invalid |
| `coinbase` | the proposer's ECDSA address, checked against the expected proposer | the proposer's `id`, same check |
| proposer selection | upstream round-robin over the ordered validator list | the same upstream rule over the list of `id`s; both clients MUST order identically (the list as carried in the parent's `extraData`) [NOT MEASURED: the upstream selector is outside the Aere overlay; the conformance vector of section 9 pins the order] |
| message authorship (proposal, prepare, commit, round-change) | the ECDSA-recovered address, which MUST be the address the registry binds to the seal's index (SPEC 2.6, rule 2) | the registry row of the seal's index; `row.id` MUST be in the validator set of the height; the upstream ECDSA signature stays on the wire (the envelope is unchanged) and its recovered address MUST equal `row.ecdsa.addr` while that field exists in the head registry, and is not examined once it does not (section 4) |
| anchor rule R2, step 8 (SPEC 3.4) | index resolves to an address that must be in the parent's validator set | index resolves to an `id` that must be in the parent's validator set; two indices resolving to one `id` reject, as today |
| genesis of a new chain | validators are ECDSA addresses | validators are `id`s and `pqRegistryHash` names a v3 registry with `bindHeight 0`; the genesis set is then post-quantum from block 0 and the `ecdsa` field is never needed |
| history | | a from-genesis node judges sets below `H_id` by addresses and above it by `id`s, with the registry history list (never pruned, SPEC 3.2) providing every epoch |
#### 3.4 Peer forwarding
The Besu-derived client forwards Istanbul messages only to peers whose RLPx node-key address is in the validator set (measured 2026-09-02 on testnet 28001: a validator whose node key differed from its validator key received no consensus messages). With `id`s in the set, that predicate has no ECDSA address to match. From `H_id` a client MUST forward to every peer that (a) has negotiated the Istanbul capability and (b) either presents a node-key address listed as `transport.secp256k1Address` in a row of the head registry whose `id` is in the set, or, when no row lists a transport address, is any connected peer with the capability. Node identity on RLPx remains secp256k1 ECDSA; this AIP does not change the transport (Security Considerations).
### 4. ECDSA demoted: the canonical finality rule
From `H_fin` (a height at or above `H_id`), on every node that validates or reads blocks:
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.
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.
### 5. ECDSA out of the header
From `H_hdr` (at or above `H_fin`), `extraData` gains element 6, `proposerSeal = [index, signature]`, a Falcon-512 seal by the proposer over the message the proposal already signs, `keccak256(RLP["AERE-PQ-PROPOSAL-1", chainId, height, round, proposalDigest])`, where `proposalDigest` is the QBFT proposal digest recomputable from the stored header (the round is element 3 of the stored `extraData`; the digest is the `EXCLUDE_COMMIT_SEALS` encoding hash, SPEC 2.3). Element 6 is outside the hash (like the commit seals and the certificate), present only when non-empty, and subject to the strict codec. From `H_hdr` a header is admitted between anchors iff the proposer seal verifies for the `coinbase` `id` at that height, and the ECDSA committed seals element MUST be empty. Cost: +663 B and -455 B (7 x 65) per block at N = 10, net +208 B, that is **+11.6 GB per node per year** (208 x 55,854,159 / 10^9). After `H_hdr` no ECDSA signature appears in any header or is examined in any consensus message; the last classical use is the RLPx transport.
### 6. Equivocation evidence and consequences without stake
**Evidence object.** `PqEquivocation = [type, height, round, payloadA, sealA, payloadB, sealB, registryProof]` where `type` is one of the four message domains, the two payloads are the upstream RLP payloads of two messages of that type at the same height and round with different digests (for round-change, different `prepared digest` at the same target round), the seals are the two Falcon-512 seals of the same index over the respective pre-images of SPEC 2.6, and `registryProof` is the v3 Merkle path of that index's row in the epoch bound at `height`. The object is valid iff both seals verify under the row's Falcon key, the index is the same, and the digests differ. A second form, `PqConflictingAnchors = [anchorHeaderA, anchorHeaderB, registryProofs]`, two anchor headers at the same height with different parent hashes and each with a valid certificate of at least Q seals: by the quorum-intersection bound proved for all N in `formal-consensus/qbft_safety_smt.py` (L1: `2Q - N >= f + 1`), the two certificates share at least f + 1 = 4 indexes at N = 10, each of which double-signed. This is accountable safety: a finality violation names its authors by their post-quantum seals, and it needs `K = Q` (section 2), which is one more reason for that step.
**On-chain verification.** An immutable, ownerless contract `PqEquivocationEvidence` accepts the object, verifies each seal with the live Falcon-512 precompile at `0x0AE1` (40,000 gas flat per verification, input framing of SPEC 4.2, live since block 9,189,161) and the SLH-DSA precompile at `0x0AE4` for a hybrid seal, verifies the Merkle path against the registry hash pinned in slot 0 of the epoch's anchor contract, and records `(index, id, height, type)` permanently. Gas per submission: 2 verifications plus calldata of roughly 2 x 663 B plus two payloads plus a Merkle path (**estimate**: under 300,000 gas; to be measured on the reference implementation).
**Consequence.** The set is permissioned and unstaked, so the only on-chain consequence available is removal: the remaining validators vote the `id` out with the QBFT vote element, and a client MAY refuse to count further messages from an index that a verified evidence record names, only from a height at which every node has the record (a schedule step, never a local decision, for the same reason as every other parameter here). A future AIP that introduces a bond can slash against the same record without a new evidence format. Stated plainly: on chain 2800 today all ten validators are operated by the Foundation, so "the remaining validators vote" is one party voting; the evidence record is nevertheless a public, independently verifiable fact, which is what this section is for.
### 7. History compression (research track, not a finality mechanism)
Once every anchor is a quorum certificate, a proof that "every anchor in `[a, b]` carried a valid certificate of at least Q seals under the registry epoch in force" lets a node prune the SLH-DSA seals of that range and keep the proof. The sizing study's arithmetic stands: at one proof per day the stored cost is 0.23 GB per node per year at any N (measured FRI size, one proof per 152,900 blocks), against 34.3 GB for the SLH-DSA seals themselves at N = 10. Admissibility criterion, from the study and binding here: the proof system's soundness MUST reduce to a plausibly post-quantum assumption (hash-based, FRI); a Groth16, PLONK or KZG proof on an elliptic-curve pairing is excluded because its soundness falls to Shor, and the adversary would forge the aggregation proof rather than a single seal. What is measured: the FRI component at production parameters (639,032 B, 6.1 to 7.1 s single thread, 80 to 91 ms verification, on a laptop). What is not: the arithmetization of Q Falcon-512 verifications (the AIR), its trace height (2^20 is an unverified assumption; 2^22 would give 773,496 B and 21 to 27 s by the measured slope), the same for SLH-DSA (a hash tower, much larger), generation on validator hardware, and multi-thread scaling. A node that syncs after pruning trusts the proof verifier and the registry binding instead of the seals; the STARK-verify precompile of AIP-16 is a skeleton that verifies nothing. Nothing in this section is on the activation path of sections 2 to 5.
### 8. Sync: what a from-genesis node verifies, and the cost
Per block: the hash chain (one keccak of the header, already done), the proposer check, and after `H_hdr` one Falcon verification (0.068 to 0.092 ms median measured on validator-class hosts in July, 0.215 ms p95; 0.367 ms median on a 2-vCPU reader host on 2026-09-22). Per Falcon anchor (every 32 blocks): up to N Falcon verifications, 0.7 ms median to 3.7 ms at N = 10 depending on the host; per hybrid anchor (every 128): additionally up to N SLH-DSA verifications, **measured** 2026-09-22 at 0.669 ms median (1.494 ms p95) each on the 2-vCPU reader host with the fleet's jars, so about 6.7 ms per hybrid anchor. Amortized per block: about 0.02 to 0.12 ms (Falcon at 32) plus about 0.05 ms (SLH-DSA at 128), against a measured import budget of about 0.77 ms per block (1,300 blocks per second with ten peers, 2026-09-11); the post-quantum verification is therefore under 10% of import time on the measured host. The earlier gas-proxy estimate (8.75 x Falcon) overstated the ratio: on the consensus path (JDK digest) SLH-DSA verification costs 1.8 times a Falcon verification on the same host. The whole history's anchors have been walked once by the independent Nethermind-derived watcher (134,954 anchors, 2026-09-08, operating notes), so the verification of every certificate ever produced is a measured, repeatable operation, not a projection. Memory: the AIP-21 side store holds about 7 kB per block for ten seals over a 256-block window (AIP-21); a syncing reader does not need it.
**Liveness at K = Q.** A proposer builds the certificate for `parent(A)` from the commit seals it heard. A proposer that finalized `parent(A)` through its own QBFT round holds at least Q of them by construction (a commit counts only with a valid seal since 17,250,000). A proposer that imported `parent(A)` by block gossip or sync, or that restarted and lost its seal store (the store is node-local and volatile; findings register, 2026-09-03 and 2026-09-05), cannot build it and its round times out; the next proposer in rotation takes the anchor. Measured before this AIP, at K = 6 with 8 to 9 seals attached: 12 of 12 anchors at round 0 (2026-09-05); 10 of 10 Falcon seals verified on every validator's AIP-21 record (2026-09-22). The round-0 rate at K = Q is [NOT MEASURED] and is Stage 1's testnet gate.
### 9. Cross-client determinism and conformance
Both clients of chain 2800 MUST: compute the same `id` from the same key; load, hash and refuse the same v3 registries; translate the set identically at `H_id`; order the validator list identically; accept and refuse the same anchors under the same scheme-interval schedule and thresholds; roll back the same provisional tail on the same anchor; verify the same equivocation objects. Conformance vectors are published with the reference implementation: a v3 registry with rotation, a set translation at a fixed height, a Falcon-only anchor, a hybrid anchor, a short certificate, a certificate with a surplus scheme, a proposer seal, an equivocation pair, a classical-only fork of 31 blocks; each invalid vector is refused by both clients for its stated reason (the lesson of AIP-20: a negative vector differs from the valid one in exactly one field, and the refusal names the field).
## Rationale
**A chained certificate instead of a per-block one.** The parent-hash chain is a commitment; a quorum certificate on it covers everything below. Writing the same seals in every header multiplies disk by the interval for no additional binding. The cost table is the argument: 45.9 GB against 259 to 370 GB per node per year.
**Two intervals instead of one.** Falcon seals are cheap to make (0.6 ms) and to store (663 B); SLH-DSA seals are expensive to make (0.3 to 0.7 s on the consensus thread) and to store (7,861 B). Coupling them at one interval forces the choice between a slow single-assumption finality (128 blocks) and an SLH-DSA disk bill four times today's. Decoupling gives 18 s single-assumption and 72 s dual-assumption finality for +8.7 GB.
**K = Q instead of a threshold below it.** Below the quorum the certificate proves participation, not consensus, and the accountable-safety bound of section 6 does not hold. The guard already permits 7 at N = 10; the cost is the liveness timing condition of section 8, bounded by a round change.
**An address derived from the Falcon key, with the AIP-20 derivation.** One derivation for accounts and validators removes a class of confusion and lets a validator spend its `coinbase` proceeds with its consensus key once Falcon transactions activate. A separate payout address was considered and rejected: it adds a field and a validation rule to every header for the sake of the transition period only.
**Rotation chain instead of re-deriving the id at every epoch.** A scheme rotation must not change the validator set; keys are what rotate, the identity is what persists, and the previous keys sign the hand-over.
**Merkle registry hash.** The flat pre-images of v1 and v2 were right for a fail-closed loader; on-chain evidence needs membership proofs, and the domain change makes the two forms unconfusable.
**Not a STARK in the finality path.** The measured FRI proof is 100 times a Falcon certificate at N = 10 and would violate the propagation rule at every N; the Falcon arithmetization is unmeasured; and an asynchronous proof cannot be a validity criterion for the block it attests. It is the right tool for pruning, later.
**Not a hybrid-flag hard removal of ECDSA on day one.** Every step here tightens what a syncing node requires and never loosens what an online node requires. Removing the envelope from the wire would change the upstream message format on both clients for no security gain; removing it from the validity rules is the security gain, and that is section 4.
**Alternatives rejected, in one line each:** per-block certificate (disk); per-block SLH-DSA (disk and period, measured); Merkle root plus sidecar for full nodes (saves nothing for a full node and moves light-client trust to data availability, study section 6; it remains the right tool for light clients and is not excluded for them); XMSS or other stateful hash-based signatures (a key exhausts in 6.9 days at the block rate and a restore from backup is a reuse, study section 2); elliptic-curve SNARK aggregation (soundness falls to Shor, study section 3); threshold Falcon (no standard).
## Backwards Compatibility
Every activation height here is a coordinated fork: the same binary and the same schedule on every node that validates or reads blocks (the validators of both clients and every reader, whoever operates it), armed before the height, one node at a time with cooling between restarts (SPEC section 2.6 and the published activation procedure): a node that judges headers on a different binary or a different schedule diverges from the validators at the height, so readers are armed with the validators, never after them. Below each height nothing changes. Headers produced before `H_hdr` keep their ECDSA seals and remain valid at their heights; the strict codec round-trips them unchanged. The public verifier `tools/verify-anchor.mjs` needs the scheme-interval schedule (Stage 2), the v3 registry and `id`s (Stage 3), and the proposer seal (Stage 5), each published in the same hour as the fleet change (a registry epoch that the public verifier does not know makes it reject every anchor the new signer takes part in). The published follower configuration in `RUN-A-NODE.md` and the SPEC gain the new properties at each stage. The ZK light client contracts (`AereZkQbftLightClient`, canonical verifiers anchored to the seven-validator set; findings register, 2026-09-05 and 2026-09-11) verify ECDSA quorums with an elliptic-curve SNARK and cannot attest post-quantum finality; after `H_fin` a light client either verifies anchor certificates directly (the published verifier already does) or waits for a hash-based proof system (section 7). Integrators that read `extraData` element 1 as ECDSA addresses must read `id`s from `H_id`; `eth_getBlockByNumber` is otherwise unchanged; `coinbase` becomes an `id`.
## Security Considerations
- **Threat model.** An adversary who holds every classical validator key and no post-quantum key (the posterior-corruption, long-range rewrite; peer-reviewed treatment of the threat: Azouvi, Danezis, Nikolaenko, "Winkle", IACR 2019/1440, AFT 2020; the quantum framing and this defence are Aere's own). "Harvest now, decrypt later" does not apply to signatures and is not what this AIP defends against.
- **What this AIP achieves against that adversary, per height.** Today: cannot finalize a block online (message seals); can serve a syncing node a tail of up to 127 blocks that the next anchor invalidates. After `H_K` and `H_I`: the same, with the tail bounded at 31 blocks and every covering certificate a quorum. After `H_id`: cannot name a validator, vote, or propose in any set, even for a syncing node. After `H_fin`: cannot cause any node to consider any block final. After `H_hdr`: cannot produce a header that any node admits at all.
- **What it does not achieve.** Node-to-node transport (RLPx) is authenticated by secp256k1; an adversary with node keys can impersonate a peer, not a validator. A post-quantum handshake (ML-KEM) is a separate AIP. Nothing here is audited by a third party; AIP-15's external-audit gate applies before `H_fin` on chain 2800. The set is ten validators operated by one party; this AIP changes what a key can do, not who holds the keys.
- **Hybrid conjunction.** A certificate is valid only with the quorum of every required scheme; forging it needs both a lattice break (Falcon-512) and a hash-function break (SLH-DSA-SHA2-128s) at dual-assumption anchors, and a lattice break alone at the intermediate Falcon anchors. The single-assumption window is at most 32 blocks by design, and the founder can shorten it at the cost in the table.
- **Liveness tax at K = Q.** With f validators down, an anchor needs every remaining seal to reach its proposer in time; a proposer without them skips by round change. The measured pre-AIP rate is 12 of 12 anchors at round 0; the post-AIP rate is a testnet gate. The threshold guard's warning text is the operator's acknowledgment of this trade.
- **Rollback of a provisional tail.** New behaviour for a QBFT client, bounded at 32 blocks, exercised on the testnet with a negative control before any mainnet height. A bug here is a liveness bug on readers, never a safety bug: nothing becomes final without a certificate.
- **Weak subjectivity.** Section 4, rule 5. A node without a checkpoint can be eclipsed onto a fork that ends before 13,034,000; with one, it cannot be made to accept a rewrite at any height.
- **Identity derivation.** Including the type byte and the algorithm identifier in the `id` pre-image (as AIP-20 does) closes "same bytes, different scheme" confusions; the `id` is not a proof of anything, the registry row is.
- **Registry writer.** A registry epoch is produced by the Foundation and pinned on chain by a transaction from a Foundation key; a rogue epoch cannot bind a key to an existing `id` without that id's previous keys signing the rotation, and cannot add a row whose key does not prove possession. It can still add a new `id` with new keys, which is the power to add a validator, and that is the governance weakness the AIP-19 process names, unchanged here.
- **Denial of service by seals.** Without the ECDSA authorship check, any connected peer can send messages that fail the Falcon check; each costs 0.068 ms to refuse and the per-peer limits of the client apply. This is the same surface the message layers already have since 17,700,000.
## Reference Implementation and On-Chain Deployment
Nothing described here exists at the Created date. Planned artifacts, each landing as an exact delta over the production tree of the Besu-derived client (`consensus-pqc/arbore-complet-2026-08-01/`, the discipline of AIP-20 and AIP-21) and as anchored, idempotent patches for the Nethermind-derived client (`nethermind-pqc/nethermind-intree/patches/`), with a two-client conformance proof and a negative control in each client before any height is set:
1. Stage 1, `H_K`: a schedule step `<H_K>:7` and cap 10 on every node; the existing gate `scripts/deschise/pragul-ancorei-e-cvorumul-setului.sh` turns green; negative control: a certificate with six seals at or above `H_K` is refused by both clients; measured round-0 anchor rate at K = Q on testnet 28001 (four Besu-derived validators, one Nethermind-derived, Q = 4 at N = 5) and then on chain 2800.
2. Stage 2, `H_I`: `aere.pq.schemeIntervalSchedule` in `PqAnchorConfig` and the producer, `AERE_PQ_SCHEME_INTERVAL_SCHEDULE` in the Nethermind engine and header validator, `SCHEME_INTERVAL_SCHEDULE` in the public verifier, the disk gate reading per-scheme intervals, the z3 model of the interval schedule extended to per-scheme grids; negative control: a hybrid-required anchor carrying only Falcon is refused, a Falcon-only anchor missing at a 32-grid height is refused.
3. Stage 3, `H_id`: registry v3 (`PqRegistryHash` v3 pre-image and Merkle root, loader, `PqRegistryBinding` rotation and per-scheme possession proofs), `id` derivation, set translation at `H_id`, authorship by row, forwarding predicate, the `formal-consensus` registry models extended (`registry_rotation_coverage_smt.py`, `registry_index_binding_smt.py`) with the rotation chain; the v3 manifest tooling under `chei-falcon/` producing the epoch from the keys already on the validators (no new key material); the public manifests index gains the v3 epoch in the same hour.
4. Stage 4, `H_fin`: the canonical rule, the provisional tail and its rollback in both clients, the checkpoint option, the reader gossip of the AIP-21 record (optional sub-step, its own conformance test); external audit of stages 1 to 4 before the mainnet height.
5. Stage 5, `H_hdr`: element 6 and the empty-commit-seals rule in the codec of both clients and in the public verifier.
6. Section 6: `PqEquivocationEvidence` under `contracts/`, with the precompile framing conversion (registry public key to the 897-byte precompile input) tested against the live precompiles on testnet 28001.
Activation heights on chain 2800 are the founder's (AIP-19 acceptance per stage); the disk delta (+8.7 GB per node per year at Stage 2, +11.6 GB at Stage 5) is stated to the founder with the fleet's measured margin at that date; the SLH-DSA verification time, the round-0 rate at K = Q, the validator-to-validator bandwidth and the Falcon arithmetization are the measurements that precede, respectively, section 8's claim, Stage 1, any set growth beyond 21 with SLH-DSA in the header, and section 7.
## Stage records (dated; the Status and Ratification rows above are unchanged by them)
**2026-09-22, stage 0, measurement 1 (SLH-DSA verification time):** see the constants table; recorded in the repository under `consensus-pqc/aip22-treapta0/`.
**2026-09-22 22:48Z, stage 1 started on testnet 28001 (K = Q = 4 of 5):** the step `2988864:4` was appended to the per-scheme minimum schedule of all six nodes that judge headers on chain 28001 (four Besu-derived validators and the read node on one host, the Nethermind-derived validator on another), one node at a time with at least 120 s and one anchor between restarts; every node caught the tip within about 20 s and carries the step in its live process environment. The Besu-derived client's threshold guard accepted the step with its quorum-or-above warning: "K reaches 4 at height 2988864; quorum for 5 validators is 4 and N - f is 4 ... the margin under the fault budget is 0", which is the liveness tax of section 8 stated by the code. Baseline before the step, measured the same hour on 40 anchors at K = 3: 40 of 40 at round 0, parent-to-anchor cycle 2.33 s mean (the testnet host is loaded; chain 2800 measured 1.80 s on 20 anchors in the same hour).
**2026-09-22 23:21Z, stage 1 proven on testnet 28001 (K = Q = 4 of 5):** over the seven anchors above H = 2,988,864, seven of seven were produced at round 0 (100%, parent-to-anchor cycle 2.29 s mean on the loaded host); the public verifier (`verify-anchor.mjs`, the tool this package ships) accepted all seven, each carrying 4 to 5 Falcon-512 and 4 to 5 SLH-DSA seals with one anchor carrying exactly the four-of-five minimum per scheme; a negative control that demanded six seals of five validators from H refused all seven; and both clients held identical hashes for all seven anchors (the Nethermind-derived validator on its local RPC and the Besu-derived nodes on the public door). Stage 2 (per-scheme intervals) is implemented in both clients with matched conformance vectors and negative controls; nothing here is on chain 2800.
**2026-09-23 10:13Z, stage 2 armed on testnet 28001 for H_I = 3,070,656:** all six nodes that judge headers carry `3070656:32` in the anchor-interval schedule and `3070656:falcon-512/32+slh-dsa-sha2-128s/128` in the scheme schedule, each switched in one restart with the house guards: the five Besu-derived nodes on a distribution built from the fleet's laboratory tree (the three consensus modules' regression 1,091 probes, 0 failed; against the live distribution exactly the seven stage-2 classes differ, none is missing), the Nethermind-derived validator on a binary built on its host (no type of the live binary missing). The public verifier reads the new grammar (a scheme named at the parent's step but off its own grid is surplus: verified, not required at K; a scheme not named at all is refused) and gave the same result as before on both chains ahead of H_I (20 of 20 on 2800, 6 of 6 on 28001).
**2026-09-23 12:07Z, stage 2 proven on testnet 28001 (H_I = 3,070,656):** over the 11 anchors from H_I to the tip (3,070,656 to 3,070,976), the three on the 128 grid (3,070,656, 3,070,784, 3,070,912) were hybrid (four Falcon-512 and four SLH-DSA-SHA2-128s seals each) and the eight between them Falcon-only (five Falcon-512 seals); eleven of eleven were produced at round 0 (parent-to-anchor cycle 1.00 s mean); the public verifier with the stage-2 schedule accepted all eleven; a negative control running the same verifier with the schedule in force before H_I (SLH-DSA required at every anchor) refused exactly the eight Falcon-only anchors ("missing scheme slh-dsa-sha2-128s") and accepted the three hybrid ones; the Nethermind-derived validator held identical hashes for all eleven; the block rhythm was unchanged (0.566 s before, 0.563 s after). Not measured, and stated: the behaviour of a node left without the stage at H_I (the testnet has none). The public verifier with per-scheme intervals and the stage-3 identity translation is published in the `aere-node` package and on the site the same hour; chain 2800 verifies 20 of 20 with it, unchanged. Nothing of stage 2 is on chain 2800.
**2026-09-23 13:33:49Z, stage 3 rehearsed on testnet 28001 (H_id = 3,080,010), with two defects of the Nethermind-derived client found and repaired the same hour:** the v3 epoch of the testnet (five rows, each binding exactly the Falcon-512 key, the SLH-DSA key and the address of the live key manifest, checked row by row; registry hash `0xd19eef4a...`, pinned in every node's configuration) was published in the testnet's manifest index before H_id, and the public verifier finds it there, verifies it in full (every proof of possession, every ECDSA link, the Merkle registry hash) and requires its keys to equal the keys that sign. At H_id the validator list in `extraData` changed from the five ECDSA addresses (block 3,080,009) to the five post-quantum ids (block 3,080,010), and `coinbase` became an id. Defect 1, found by reading the code at 12:48Z, before H_id: the Nethermind-derived client's anchor rule and producer compared the registry's classical address of a seal's index with the parent's list, which holds ids from H_id, so it would have refused every anchor after H_id; repaired (the index's address translated into the identity of the parent's height, as the Besu-derived client does) and switched at 12:51Z. Defect 2, found on the chain: that client reset its message-author check at every height to the validator set of its environment, in addresses, so from H_id it dropped every Besu message as "not in the validator set", stopped sealing, and its round-0 slot expired at every rotation: 1.546 s per block from 13:34 to 13:48Z against 0.564 before (the proof over [H_id, H_id + 500] reports it: OPEN, four proposers, rhythm ratio 2.741, kept dated). Repaired (the environment set translated into ids from H_id) and switched at 13:48:50Z. After the repair, over [3,080,800, 3,081,300]: all five ids proposed exactly 100 blocks each, both clients held identical hashes for every block from H_id - 1 to 3,081,300 (1,292 of 1,292, the degraded window included), the rhythm ratio was 1.000, the public verifier run as a stranger (no variables) accepted 20 of 20 anchors with the v3 link, and the same verifier with the identity switched off refused all 20 ("keys not in validator set"). A vote carried an id: validator 0 proposed the removal of validator 3's id, block 3,082,048 carries `[id, drop]` in its vote element, the vote alone has no majority and the set stayed five ids, and after the vote was discarded the next block of validator 0 carries none; both clients agree on those blocks. Not measured, and stated: the behaviour of a node left without stage 3 at H_id. The Besu-derived client and the public verifier also carry stage 3b (the keys that seal read from the v3 epoch in force from a height H_k, with a startup continuity guard and the ECDSA link inherited by id), proven with the shared vectors and negative controls, deployed nowhere; the local signing key by height (stage 3b-ii) is next.
**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, 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`):
```
row pre-image = "AERE-PQ-ROW-3" || u8(3) || u64be(chainId) || u64be(bindHeight) || u32be(count)
|| u32be(index) || id(20) || u8(nKeys)
|| per key, ascending scheme wire id (falcon-512 = 1, slh-dsa-sha2-128s = 2):
u8(wireId) || u32be(len pk) || pk || u32be(len pop) || pop
|| u8(hasTransport) [|| transport(20)]
row hash = keccak256(row pre-image) (rotation and ecdsa are not part of it)
Merkle node = keccak256(0x01 || left || right); an odd node is paired with itself
registry hash = keccak256("AERE-PQ-REGISTRY-3" || u64be(chainId) || u64be(bindHeight) || u32be(count) || root)
pop message = keccak256(the pre-image of section 3.2's pop row) signed by that key
rotate message = keccak256("AERE-PQ-ROTATE-3" || new row hash) signed by every previous key of the id
ecdsa claim = keccak256(0x19 || 0x00 || low20(keccak256("AERE-PQ-CLAIM-1" || u64be(chainId))) || row hash)
```
Two refinements to the text above, recorded rather than silently applied: the Merkle inner node carries the tag byte `0x01`, so an inner node can never be presented as a row hash; and a membership proof is valid only for an index below `count`, because with the odd node paired with itself the path of the last row of an odd-sized tree would otherwise also prove the index `count` (both clients and the JavaScript tool refuse it, and a probe in each asserts the refusal). Negative controls: in the Besu-derived client, removing the rotation rule, the id-derivation rule or the index bound each turns its probes red with the probes run (and the untouched source green); in the Nethermind-derived client, removing the rotation rule turns 2 of its 3 probes red and removing the index bound 1 of 3 (untouched: 3 of 3 green); in the JavaScript tool the same three plantings are caught by 2, 2 and 1 vectors. Nothing of stage 3 is wired into consensus yet: the set translation at `H_id`, authorship by row, `coinbase` and votes by id, and the forwarding predicate are the next parts, and the v3 epoch of testnet 28001 needs the PROBA keys on its hosts to sign possession, claim and rotation.
## Errata
None.
## Post-Acceptance Outcome Record
Not accepted; nothing to record.
## Copyright
Released to the public domain (CC0). No rights reserved.