diff --git a/SPEC.md b/SPEC.md new file mode 100644 index 0000000..e7330a6 --- /dev/null +++ b/SPEC.md @@ -0,0 +1,951 @@ +# Aere Network chain 2800: full protocol specification + +Version: 1.0.0, written 2026-08-25. Scope: everything a third party needs to implement a client +that follows chain 2800 from block 0 to the live head and validates what the network validates, +without reading Aere source code. It specifies the follower (validation) protocol completely; it +describes seal production only where a follower must understand it to validate. + +## 0. Conventions, and how every statement here is grounded + +Every normative statement in this document carries one of three markers: + +- **(source: `path`)**: the statement is checkable against a file published in this bundle (or, + where explicitly said, in the public Aere repositories). The file is the authority; if this + document and the file ever disagree, the file wins and the disagreement is a bug in this document. +- **[MEASURED yyyy-mm-dd]**: the statement is an observation of the live chain or of a real run, + made on that date, with the method stated. A measurement describes the chain as it was on that + date; it is not a promise about later heights. +- **[NOT MEASURED]**: the statement could not be established from the published sources and is + flagged rather than guessed. Section 9 collects all of these. + +Where two sources of truth diverge, the divergence itself is specified, with dates, in section 8. +This chain has several such places and a client that "corrects" any of them will fail to follow the +chain. **Read section 8 before implementing anything.** + +Number formatting: heights are written with commas for readability (13,014,000); in code and +configuration they are plain digits. All hashes are lower-case hex. "H" always means the anchor +activation height 13,014,000. No statement in this document uses the phrase "no block finalizes +without a post-quantum quorum"; the defensible claim is narrower and is stated exactly in +section 3.1. + +Primary sources in this bundle: + +| Topic | File | +|---|---| +| Genesis, byte for byte as the fleet runs it | `genesis.json` | +| Operator instructions, follower configuration | `RUN-A-NODE.md` | +| Client changes vs upstream Besu | `patches/0001..0005`, `precompiles/`, `anchor/` | +| Falcon validator registries | `registries/` | +| NIST test vectors for the precompiles | `vectors/` | +| From-genesis import proof and its measured boundaries | `IMPORT-PROOF-STARE-2026-08-15.md` | +| Formal verification runner and models | `../aere-research/formal-consensus/` | + +--- + +## 1. Chain parameters + +### 1.1 Identity + +| Parameter | Value | Ground | +|---|---|---| +| Chain id | 2800 (0xaf0) | (source: `genesis.json` `config.chainId`) | +| Network id | 2800 | (source: `RUN-A-NODE.md`, `--network-id=2800`) | +| Genesis file sha256 | `361709dccec4e9fc85be5aec33b30b58a55dc53424087d84c1ad11bd1d944c24` | (source: `RUN-A-NODE.md`; hash of the published file with LF line endings) | +| Block 0 hash | `0xd86d57a899cbfa580669f0e1c7cd4ae5f525247c3c823f8f75df176a0c5d7f1a` | [MEASURED 2026-08-03, `eth_getBlockByNumber("0x0")` against a from-genesis node and both public endpoints; recorded in `RUN-A-NODE.md`] | +| Consensus | QBFT (Hyperledger Besu implementation) | (source: `genesis.json` `config.qbft`) | +| Native token | AERE, 18 decimals, total genesis supply 2,800,000,000 | (source: `genesis.json` `alloc`; the six balances sum to 2.8e27 wei) | + +The block 0 hash covers every consensus-relevant field of `genesis.json` including the state root +of the six `alloc` accounts, so matching it is the complete genesis check (source: `RUN-A-NODE.md`, +"Check that you are on the right chain"). + +The `comment` strings inside `alloc` are not consensus data, are read by no client, and two of them +are textually wrong; they are published unaltered because the file is published byte for byte as the +fleet holds it. See section 8, D7 (source: `RUN-A-NODE.md`, "A note on the comment fields"). + +### 1.2 Genesis header fields + +(source: `genesis.json`) + +| Field | Value | +|---|---| +| `timestamp` | `0x69fbd960` | +| `gasLimit` | `0x1fffffffffffff` | +| `difficulty` | `0x1` | +| `mixHash` | `0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365` (the fixed BFT mix hash; constant on every block) | +| `coinbase` | `0x0000000000000000000000000000000000000000` | +| `nonce` | `0x0` | +| `extraData` | QBFT extra data (section 2.3) carrying vanity of 32 zero bytes and the three genesis validators | + +Genesis validator set, decoded from `extraData` (source: `genesis.json`): + +``` +0xb5e768cb3117ea25175f51aa956b48691025c2a8 +0xf73bbbd26a82dbc769066550d8d4585eab2fc00d +0x4bf6f91d84b37454544279cba152581ed6820044 +``` + +The genesis validator set has N=3, so its QBFT quorum is 2. This matters to a from-genesis follower +because of the anchor threshold guard: see section 6.4. + +### 1.3 EVM fork schedule + +(source: `genesis.json` `config`) + +| Fork | Activation | +|---|---| +| Homestead through London, Shanghai | block 0 / time 0 (all pre-London blocks fields are 0, `shanghaiTime` 0) | +| Cancun | timestamp 1780189051 | +| Prague | timestamp 1780189051 (same instant as Cancun) | +| Osaka | timestamp 1780220351 | +| futureEips (the Aere milestone) | timestamp 1783820272 | + +**The `futureEips` milestone is NOT upstream Besu's futureEips.** Upstream bases it on the +unreleased Amsterdam definition (Block Access Lists, a different state-root committer). Chain 2800 +isolates it on the Osaka definition, and additionally activates in that same fork (a) the five Aere +post-quantum precompiles (section 4) and (b) EIP-2935 historical block hashes, which performs one +system state write per block into the ring-buffer contract at +`0x0000F90827F1C53a10cb7A02335B175320002935` from the activation block onward (source: +`patches/0005-aere-eip2935-futureeips.patch`, header). A client that carries the standard Osaka +rules, the five precompiles and the EIP-2935 write from this milestone computes the fleet's state +roots; a client that carries upstream's Amsterdam-based futureEips does not. + +The first block at or after `futureEipsTime` is block **9,189,161** [MEASURED 2026-08-15, real +from-genesis sync of a build without patch 0005 stopped at exactly that height with +`failed persisting block due to stateroot mismatch`; recorded in +`IMPORT-PROOF-STARE-2026-08-15.md` and `patches/0005` header]. + +Patch 0001 additionally repoints the futureEips precompile registry from the Cancun set to the full +Osaka set, which is a behaviour change beyond the five new precompiles (source: `README.md`, "The +patch"). + +### 1.4 Block period and the transition every reader must not "fix" + +(source: `genesis.json`) + +- `config.qbft.blockperiodseconds` = 1, `epochlength` = 30000, `requesttimeoutseconds` = 4. +- `config.transitions.qbft[0]` = `{ "block": 2138451, "xblockperiodmilliseconds": 500 }`: from that + height the target block period is 500 ms. + +**The configured transition height 2,138,451 does not match the measured chain.** Sampling header +timestamps on the public endpoints puts the actual rate change just after block **2,137,652**: +twenty-block spans take 20 s from 2,137,600, 15 s from 2,137,656, 12 s from 2,137,657 and a flat +10 s from 2,137,700 onward [MEASURED, recorded in `RUN-A-NODE.md`, "A number in this file that the +chain does not agree with"]. The configured value is roughly eight hundred blocks late. + +**Normative consequence: ship 2138451, exactly as the fleet does.** The field only governs how fast +a proposer may seal; it has no effect on a node that follows, and a from-genesis sync carrying this +exact value imported the blocks on both sides of both numbers with zero rejections +[MEASURED 2026-08-03, sync run in `RUN-A-NODE.md`]. A genesis file "corrected" to 2137652 hashes +differently, will never peer, and any coordinated change to the live value would be a consensus +infrastructure change. Answer "2,137,652" to the question "when did the chain accelerate"; answer +"2138451" to the question "what does the live configuration say"; never merge the two. + +### 1.5 `pqRegistryHash` in the genesis config + +(source: `genesis.json` `config.pqRegistryHash`) + +``` +block 13014000 hash 0xa96ac96dcd4f1671aec8b6063cee17a3341314c92e1dd2623bc6500b7e28b0cb +block 13600000 hash 0x5e6ac058b22beaa895469671308548dfd44fbaa9ca7ea30b3dafa950c359b9b3 +``` + +This is a height-keyed schedule of canonical registry hashes (computation defined in section 3.6). +A node compares the canonical hash of the Falcon registry it actually loaded against the entry +active at its head and refuses to start on a mismatch (`AERE-PQC-REG-MISMATCH-01`); heights below +the first entry are unenforced, so nothing historical is invalidated (source: +`anchor/consensus/common/.../PqRegistryHash.java`, class javadoc). The guard runs at startup, not +per block; a per-block header rule for the same binding does not exist today (source: same file, +"WHAT THIS CLASS DOES NOT DO"). + +--- + +## 2. Consensus: QBFT + +### 2.1 Engine and parameters + +Chain 2800 runs QBFT as implemented in Hyperledger Besu at upstream commit +`d2032017bb3b8cb215a97303980a1e4a643f7180` (source: `README.md`, "The patch"; that commit is the +base of every patch in this bundle and the commit named in the artifact running on the validators). +This specification does not restate the QBFT message protocol (PREPREPARE / PREPARE / COMMIT / +ROUND-CHANGE); it is upstream Besu behaviour at that commit. What is specified here is everything +Aere adds, changes, or that a follower must check. + +- Block period: section 1.4. Empty blocks are produced continuously; the chain does not pause when + there are no transactions. +- Difficulty is the constant 1 on every block, enforced by a `ConstantFieldValidationRule` in the + QBFT header ruleset (source: `anchor/consensus/qbft/.../QbftBlockHeaderValidationRulesetFactory.java`, + the rule at the line registering `"Difficulty" ... UInt256.ONE`). Total difficulty is therefore + height + 1, and the longest chain is the heaviest. +- Epoch length 30000: pending validator votes reset each epoch (upstream QBFT semantics; parameter + in `genesis.json`). +- Validator set changes are made by the standard QBFT voting mechanism (the vote element of + extraData, section 2.3). The set had 3 validators at genesis (source: `genesis.json`), and the + live chain seals with nine validators, seven until 2026-08-09 (source: `RUN-A-NODE.md`, "What the + two bootnodes are"). The block-by-block vote history is on the chain itself and is not restated + here. + +### 2.2 Quorum + +The QBFT commit quorum is `ceil(2N/3)`: 2 at N=3, 5 at N=7, 6 at N=9. This is the value the formal +models prove is the exact safety threshold (quorum intersection greater than f for all N, and the +negative control shows a majority quorum forks) (source: +`../aere-research/formal-consensus/qbft_safety_smt.py`, property L1, and +`../aere-research/formal-consensus/RULEAZA-TOT.md`). Fault tolerance at N=9 is f=2. + +### 2.3 The extraData structure, with the Aere sixth element + +`extraData` is an RLP list. Layout (source: +`anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java`): + +| Index | Element | Encoding | +|---|---|---| +| 0 | `vanityData` | exactly 32 bytes. Historically arbitrary (the client writes a version string); at anchor heights from H it carries the anchor digest D (section 3.3) | +| 1 | `validators` | RLP list of 20-byte addresses | +| 2 | `vote` | empty RLP list when no vote; else `RLP[recipient(20 bytes), voteValue]` with the add/drop byte values defined in upstream Besu's `Vote` class | +| 3 | `round` | int scalar | +| 4 | `committedSeals` | RLP list of ECDSA (secp256k1) commit signatures | +| 5 | `falconSeals` (Aere, optional) | RLP list of `[validatorIndex intScalar, signature bytes]` pairs; present ONLY in the stored-header encoding and ONLY when non-empty | + +Three encodings exist, and which elements they carry is consensus-critical (source: same file, +`encode(...)`): + +- `ALL` (stored header): all six elements; element 5 omitted when the certificate is empty. +- `EXCLUDE_COMMIT_SEALS` (the ECDSA commit-seal signing pre-image): round kept, committedSeals + written as an empty list, falconSeals omitted. +- `EXCLUDE_COMMIT_SEALS_AND_ROUND_NUMBER` (the block-hash pre-image): round forced to 0, + committedSeals an empty list, falconSeals omitted. + +**The block hash is computed over a RE-ENCODE of the DECODED extraData in the +`EXCLUDE_COMMIT_SEALS_AND_ROUND_NUMBER` form, not over the stored bytes.** Consequences a client +must implement and must not "fix": + +1. The ECDSA commit seals, the round number, and the entire Falcon certificate are NOT covered by + the block hash. Stripping or replacing the certificate leaves the hash unchanged (it is caught + by validation instead: section 3.4). +2. Two correct neighbours can legally store different extraData bytes for the same block hash: + every node assembles its final header locally from the commits it heard. Measured on the live + chain: over 200 consecutive headers read simultaneously from two Aere RPC nodes with identical + block hashes 200/200, the ECDSA seal ORDER differed on 68 headers and the seal SET differed on + 11 (source: `anchor/consensus/common/.../PqAnchor.java`, class javadoc, which cites this + measurement as the reason the anchor's design is what it is). A client must never treat + byte-equality of stored extraData across peers as a validity criterion. + +The upstream hashing routine itself (`BftBlockHashing`) is not part of this bundle's overlay; the +property was verified by reproducing, byte for byte, the live node's reported hash for two real +chain-2800 headers from the re-encoding described above [MEASURED 2026-08-02, model recorded in the +repository's `consensus-pqc/dovezi-preimagine-element6-2026-08-02/` evidence set; see also section +9, G2]. + +### 2.4 Decoder strictness (Aere change, applies at every height) + +Upstream Besu ends the extraData decode with a lenient list-leave that silently discards trailing +content. Chain 2800's client makes two changes, both consensus-relevant to anyone re-implementing +the codec (source: `QbftExtraDataCodec.java`, `decodeRaw`, the two commented gates): + +1. **Strict leave**: any bytes left over in the outer list after the known elements is a decode + error, so a header that smuggles extra content after the certificate does not share a hash with + a clean header. +2. **Canonical round trip**: the decoded structure re-encoded in `ALL` form must reproduce the + received bytes exactly, or the header is rejected. This closes, among other shapes, the + "explicit empty Falcon list" ambiguity: an empty certificate must be OMITTED, and a header that + carries an explicit empty list is non-canonical and rejected. Every header any Aere or stock + Besu node ever produced round-trips unchanged, so the whole pre-anchor history still decodes. + +### 2.5 What QBFT header validation on chain 2800 checks, and one thing it does not + +The QBFT ruleset validates ancestry, timestamp, gas usage and gas-limit bounds, the constant +difficulty, mixHash, coinbase, the validator set and the ECDSA commit seals, plus the Aere rules of +section 3. It does **not** recompute the EIP-1559 base fee at import: Besu's +`BaseFeeMarketBlockHeaderGasPriceValidationRule` is registered only by the Clique and Merge +rulesets, not by QBFT, upstream or in this fork [MEASURED 2026-08-02, by reading the ruleset +factory and grepping `consensus/`; recorded in `RUN-A-NODE.md`, "Which Besu", and in +`../../docs`-level runbook history]. The base fee is ratified through the quorum's commit seals +over the block hash, which commits to it. Section 5 states what this means for a client that DOES +validate base fees. + +--- + +## 3. The post-quantum certificate anchor + +### 3.1 The claim, stated exactly + +From block 13,014,000, every 32nd header of chain 2800 (an "anchor block") carries a certificate +of validator Falcon-512 seals over its parent, and the anchor block's keccak block hash covers a +32-byte commitment to that certificate. The enforced minimum number of valid seals is a height +schedule: 0 from 13,014,000 (warm-up), 3 from 13,034,000 (f+1 of nine; enforced on the fleet since +2026-08-14), and **6 of 9 from block 14,961,456** (a full 2f+1 quorum, the same count QBFT itself +requires) (source: `anchor/README.md`, "Scope boundary"; `RUN-A-NODE.md`, the anchor section). + +The defensible sentence, and the only form this document endorses: *every 32nd block does not +finalize without a certificate of at least six of nine valid Falcon-512 validator seals under its +hash (three, before block 14,961,456; see the schedule and the historical windows)*. Blocks between +anchors carry no Falcon seals and are finalized by ECDSA alone. Consensus (proposal, identity, +transport, per-block finality) is classical secp256k1 ECDSA; the anchor is a binding, not a +replacement, and "post-quantum consensus" without this qualification overstates (source: +`anchor/README.md`). None of this code has been audited by a third party (source: `anchor/README.md`, +"What is not proven here"). + +The threat model is retroactive rewriting (the long-range / posterior-corruption attack), not +"harvest now, decrypt later", which does not apply to signatures. Peer-reviewed treatment of the +threat: Azouvi, Danezis, Nikolaenko, "Winkle", IACR 2019/1440, AFT 2020; the quantum framing and +this particular defence are Aere's own (source: `anchor/README.md`, which also places the design +against the four known mitigation families and states what it does NOT claim, including slashable +safety). + +Security property bought by the interval: an adversary holding every classical validator key and no +Falcon key can rewrite at most the tail since the last anchor, so fork depth <= 32 blocks (about +16 seconds); rewriting anything below an anchor changes that anchor's parent hash, forces the +anchor to be reproduced, and reproducing it requires Falcon signatures the adversary does not have. +History below H remains protected by ECDSA alone, permanently (source: +`anchor/consensus/common/.../PqAnchorConfig.java`, interval javadoc; `anchor/README.md`). + +### 3.2 Activation is by node configuration, not by genesis, and that is a real caveat + +All anchor parameters are read from JVM system properties (`BESU_OPTS`), with environment-variable +twins, not from the genesis file. **There is no consensus binding on these values: two nodes +configured with different H or different K schedules disagree about which headers are valid.** The +code says this about itself and logs it at every armed start (source: `PqAnchorConfig.java`, class +javadoc "HONEST LIMITATION" and the ARMED log line). The published follower configuration in +`RUN-A-NODE.md` is therefore normative for anyone joining the network, and this section specifies +exactly those values. + +The canonical follower configuration (source: `RUN-A-NODE.md`, "The configuration a follower +passes"): + +``` +-Daere.basefee.floor.forkBlock=10141734 +-Daere.basefee.floor.value=1000000000 +-Daere.pq.chainId=2800 +-Daere.pq.anchorBlock=13014000 +-Daere.pq.anchorInterval=32 +-Daere.pq.anchor.maxSeals=9 +-Daere.pq.anchorMinSeals=13014000:0,13034000:3,14961456:6 +-Daere.pq.anchor.minSealsCeiling=1 +-Daere.falcon.anchor.address=0x5214F0e53899Dad805ef3AdF0624F5e49d2a5EB5 +-Daere.falcon.anchor.block=13889290 +-Daere.falcon.manifest=/manifest-13600000.json +-Daere.falcon.registry.history=/manifest-13014000.json,/registru-2800-v2-13600000.properties +``` + +Configuration loading is fail-closed on PRESENCE: if no `aere.pq.*` name is set at all, the node +behaves exactly like stock Besu (the anchor never activates); if at least one is set, every field +is read strictly and any fault refuses startup with code `AERE-PQC-ANCHOR-CONF-01`, except through +the `aere.pq.anchor.disable` brake (source: `PqAnchorConfig.java`, `fromSystemConfiguration`). The +loader also refuses an armed schedule whose highest effective K is zero +(`AERE-PQC-ANCHOR-CONF-01/minSealsFloor`), and a proposer seal cap below the highest scheduled K +(source: same file). + +Semantics of each parameter (source: `PqAnchorConfig.java`): + +- `aere.pq.anchorBlock` (H): below H both anchor rules return true before decoding anything; the + historical chain is untouched. +- `aere.pq.anchorInterval` (32): anchor heights are counted FROM H: + `(n - 13014000) % 32 == 0`, equivalently `n mod 32 == 16`. H itself is an anchor height. Between + anchor heights the rules judge nothing. +- `aere.pq.anchorMinSeals`: `height:K` steps, floor semantics via `floorEntry(height)`; a step must + exist exactly at H; steps below H refuse. +- `aere.pq.anchor.minSealsCeiling`: effective K is `min(scheduled, ceiling)`. The ceiling can only + lower K, never raise it (proved: `../aere-research/formal-consensus/pqanchor_ceiling_monotonicity_smt.py`). + Why a follower sets 1: section 6.4. +- `aere.pq.anchor.maxSeals`: proposer-side cap on how many seals are WRITTEN. K is a floor, not a + cap: a proposer includes every eligible seal it heard in time, so real anchor headers carry more + than K (measured seal counts at K=3, N=7: 42 blocks with 4 seals, 36 with 5, 5 with 6; a + Falcon-512 seal is 666 bytes) (source: `PqAnchorConfig.java`, `PROPERTY_MAX_SEALS` javadoc, + measurement dated 2026-08-07). Real anchor blocks carry three to nine seals (source: + `RUN-A-NODE.md`). +- `aere.pq.anchor.disable`: local emergency de-arm; both rules off on this node; per-block disarm + announcement keeps shouting. Local on purpose: a halted chain cannot deliver an on-chain change. +- `aere.pq.chainId`: enters the D and M pre-images (domain separation from the proving chain + 442807, which runs the same binaries and may run the same keys). +- `aere.falcon.manifest` + `aere.falcon.anchor.address`/`anchor.block`: head registry wiring; + `aere.falcon.registry.history`: every earlier registry, never pruned (a from-genesis node + verifies every block ever produced) (source: `registries/README.md`, `RUN-A-NODE.md`). +- `aere.falcon.forkBlock`: a validator-side legacy per-block rule; see section 8, D6. A follower + leaves it unset. + +An armed node refuses to start in any sync mode other than FULL (`PqAnchorSyncModeGuard`, +`REQUIRED_MODE = "FULL"`) (source: +`anchor/consensus/common/.../PqAnchorSyncModeGuard.java`), because under light validation modes the +seals rule does not run. + +### 3.3 The two pre-images and the certificate encoding + +(source: `anchor/consensus/common/.../PqAnchor.java`; these are exact and normative) + +``` +D = keccak256( RLP[ "AERE-PQ-ANCHOR-1", chainId, parentNumber, parentHash, C ] ) +M = keccak256( RLP[ "AERE-PQ-COMMIT-1", chainId, blockNumber, blockHash ] ) +C = RLP[ [idx, sig], [idx, sig], ... ] indices STRICTLY INCREASING +``` + +- The domain labels are exactly 16 ASCII bytes each, written as RLP byte strings. `chainId`, + `parentNumber`, `blockNumber` are RLP long scalars; the hashes are 32-byte strings. +- **D** occupies all 32 bytes of the anchor block's `vanityData`: no magic, no version byte, no + padding. Format versioning is by activation height, not by an in-band byte. +- The certificate C carried by anchor block N attests to N's PARENT: each seal's signature is a + Falcon-512 signature over `M = commitMessage(chainId, N-1, hash(N-1))`. M is a function of the + block hash, deliberately not of the QBFT commit hash (which includes the round), so any node + holding the parent header can recompute it with no state and no round memory. +- C is hashed into D IN THE ORDER CARRIED, and both validation rules require strictly increasing + indices, so for every accepted header "the order given" and "sorted" are the same list. Strictly + increasing indices also make duplicate indices unrepresentable (the "repeat one seal to inflate + k" attack is excluded by the grammar). +- The certificate is CARRIED (element 5 of extraData, outside the hash); only its 32-byte digest D + (element 0, vanityData) is under keccak. Change one byte of the certificate and D no longer + matches; strip the certificate entirely and the hash is unchanged but validation rejects the + header (digest of the empty certificate differs, and the seal count is below K). +- Seal signature bytes are in Bouncy Castle's Falcon signature encoding, + `(0x30+logn) || nonce(40) || compressedSignature` (0x39 leading byte for Falcon-512); a seal is + 666 bytes (source: verification call in + `anchor/consensus/common/.../FalconSealSupport.java` (BC `FalconSigner.verifySignature` over the + 32-byte M), encoding stated in `precompiles/AereFalconSupport.java` javadoc; length measured in + `PqAnchorConfig.java` javadoc). Falcon signatures are variable-length by nature; the RLP byte + string carries the exact length. + +At H itself the parent predates the scheme, the certificate is legitimately EMPTY, and vanityData +carries the digest of the empty certificate; K=0 makes that acceptable only during the warm-up step +(source: `anchor/consensus/qbft/.../PqAnchorSealsRule.java`, the `k == 0` branch). + +### 3.4 What an armed node rejects: the validation rules + +Registered in the QBFT header ruleset alongside the classical rules (source: +`anchor/consensus/qbft/.../QbftBlockHeaderValidationRulesetFactory.java`; registration is itself +tested with a measured negative control, `QbftAnchorRuleWiringTest`, because Besu wraps detached +rules in lambdas that hide them from reflection): + +**R1, `PqAnchorDigestRule`** (detached, runs in light validation; twin `PqAnchorDigestAttachedRule` +carries the same verdict on the attached path so it survives `SKIP_DETACHED` import): + +1. Below H, or at a non-anchor height, or when the height is inside the disarmed historical window: + accept (window handling in section 8, D4). +2. vanityData must be exactly 32 bytes; the certificate's indices must be strictly increasing + (inside the disarmed window: distinct and non-negative suffices); recompute + `D' = anchorDigest(chainId, N-1, header.parentHash, carried certificate)` and require + `D' == vanityData`. Any mismatch, and any undecodable extraData, rejects: the exception path + returns false, not true (source: `anchor/consensus/qbft/.../PqAnchorDigestRule.java`). + +**R2, `PqAnchorSealsRule`** (attached; needs the parent and state, runs on the import path). In +order (source: `anchor/consensus/qbft/.../PqAnchorSealsRule.java`, `validate`): + +1. Below H or non-anchor height: accept. Inside the disarmed historical window: accept as written + after checking the certificate still decodes with distinct non-negative indices. +2. The supplied parent must be the parent the header names (number N-1, matching hash), else + reject. +3. Decode extraData (strict codec, section 2.4); let k = number of carried seals. +4. Compute the required threshold: `required = min(schedule at N, emergency ceiling)`; inside the + threshold-only historical window, `required = min(required, 1)` where 1 is the measured floor + that was actually in force (section 8, D5). +5. `k < required` rejects. +6. Indices not acceptable (strictly increasing; window relaxation as above) rejects. +7. `k == 0` accepts here (only reachable when required == 0; R1 has already bound the digest of the + empty certificate). +8. For each seal: resolve `index -> validator address` in the registry AT THE PARENT's height + (N-1); an unbound index rejects; an address not in the PARENT block's validator set rejects; two + indices resolving to one address rejects; a Falcon-512 verification failure of the signature + over M rejects. Registry resolution at the parent height is exact and has a one-block boundary + case at H: a registry bound at exactly `parentNumber + 1` answers for the parent, because the + schedule binds inclusively from H while seals commit to H-1. Without this alignment a + from-genesis node rejects the first anchor; with it, nothing else changes + [MEASURED 2026-08-15, from-genesis import; boundary and negative control recorded in + `IMPORT-PROOF-STARE-2026-08-15.md`]. +9. Any exception during checking rejects (fail-closed). + +**`PqRegistryBindingRule`** and **`PqEmergencyShoutRule`** are also registered: the former is part +of the registry-binding machinery, the latter only logs (it makes a disarmed-but-configured node +shout at every block) (source: `QbftBlockHeaderValidationRulesetFactory.java`). + +**`FalconSealValidationRule`** (the legacy per-block rule) retires at H: its first statement +accepts any header at `number >= legacyFalconRuleRetirementBlock()`, which equals H whenever the +anchor is configured. Exactly one of the two regimes is in force at any height (source: +`PqAnchorConfig.java`, `legacyFalconRuleRetirementBlock`; +`anchor/consensus/qbft/.../FalconSealValidationRule.java`). See section 8, D6 for why this matters +historically. + +The rejection is a real import failure: on a live test network the log sequence is +`PqAnchorSealsRule ... REJECTED` then `MainnetBlockValidator | Invalid block` then +`InvalidBlockException` then sync-target disconnect [MEASURED 2026-08-06, armed test network, +recorded in the repository's `consensus-pqc/controale-finale-2026-08-06/` evidence; 12 of 12 +stripped-certificate headers rejected on the 4-node rehearsal network, source: `anchor/README.md`, +"What is proven"]. + +### 3.5 The Falcon validator registries + +(source: `registries/README.md`, files in `registries/`) + +A node cannot validate any anchor block without the registries: it imports fine to 13,014,000 and +then refuses every anchor with `AERE-PQC-REG-BLOCK-01`, deliberately and fail-closed, because seal +indices cannot be mapped to validator addresses. + +| File | Bind height | Keys | Role | sha256 | +|---|---|---|---|---| +| `manifest-13014000.json` | 13,014,000 | 7 | registry in force from the first anchor; carried in history | `006b1b3fb814ed84622539160956263faca7c0b76585c22f2e8acb926c0bcb7f` | +| `registru-2800-v2-13600000.properties` | 13,600,000 | 9 | nine-validator registry from the set expansion; carried in history | `c9a215d294e106f69fb2f16e3af21f9787523969e4b7260a3098f4b2cf458059` | +| `manifest-13600000.json` | 13,600,000 | 9 | head registry, passed as `aere.falcon.manifest` | `23736b7d4ba1d3213496f8b5a5f756d5c958b08efebdeb1329b24d7dfa45fb6e` | + +Each JSON manifest carries `formatVersion` (2), `chainId` (2800), `bindHeight`, `count`, and rows +keyed `"0".."count-1"`, each row holding: + +- `pk`: the validator's Falcon-512 public key, stored as the raw `h` polynomial, 896 bytes + (NOT the 897-byte standard encoding with its one-byte header; the header is absent in these + files) (source: `PqRegistryHash.java`, `FALCON_512_PK_LENGTH` javadoc); +- `addr`: the validator's chain-2800 address; +- `pop`: a Falcon proof of possession signed by the row's own key; +- `claim`: an ECDSA signature by the validator's own account key, the half a registry writer cannot + forge, closing row re-binding and row swapping. + +No private key material exists in these files. Provenance of the published bytes: each file was +read from three independently operated validator hosts and produced the same sha256 on all three +(source: `registries/README.md`). + +Registry rotation is height-scheduled and every height has exactly one registry (proved: +`../aere-research/formal-consensus/registry_rotation_coverage_smt.py` and +`registry_index_binding_smt.py`). The head registry is additionally pinned by an on-chain anchor +contract at `0x5214F0e53899Dad805ef3AdF0624F5e49d2a5EB5` from block 13,889,290 (source: +`RUN-A-NODE.md`, follower configuration; the contract's own interface is not specified in this +bundle, see section 9, G6). + +### 3.6 The canonical registry hash + +(source: `anchor/consensus/common/.../PqRegistryHash.java`; exact and normative) + +v1 pre-image (domain `AERE-PQ-REGISTRY-1`): + +``` +"AERE-PQ-REGISTRY-1" 18 bytes ASCII +uint64be(chainId) 8 bytes +uint32be(count) 4 bytes +for i in 0..count-1: + uint32be(i) 4 bytes + uint8(addressBound?1:0) 1 byte + address 20 bytes, only when address-bound + uint32be(len(publicKey)) 4 bytes + publicKey len bytes +``` + +v2 pre-image (domain `AERE-PQ-REGISTRY-2`; address binding mandatory; adds `uint64be(bindHeight)` +after chainId, and per row appends length-prefixed `possession` and `claim` proofs). The canonical +hash is keccak256 of the pre-image, rendered as 64 lower-case hex digits without `0x`. The proofs +are INSIDE the hash so that stripping them yields a different registry with a different hash, and +the two domains ensure a v1 file can never satisfy a v2 schedule entry. The `pqRegistryHash` values +in `genesis.json` (section 1.5) are hashes under this scheme; index, chainId and bindHeight are all +inside the pre-image, so row permutation, cross-chain reuse and height re-binding all change the +hash. + +Registry loading is strict: `count` must be present, indices exactly `0..count-1` with no gap and +no extra, address binding all-or-nothing, hex even-length and strictly hexadecimal; an ambiguous +file refuses rather than hashes (source: `PqRegistryHash.java`, `Registry` javadoc). + +### 3.7 Guards a validator-capable build enforces at startup + +Stated because a third-party implementation that produces blocks needs them, and a follower needs +to know they exist: + +- `PqAnchorThresholdGuard`: refuses to arm a seal threshold at or above the QBFT quorum of the + CURRENT validator set (a proposer could never gather it), and refuses any threshold above + N - f = 7 at N=9 (source: `anchor/consensus/common/.../PqAnchorThresholdGuard.java`; + `anchor/README.md`; `RUN-A-NODE.md`, "Why the seal-threshold ceiling"). The refusal code is + `AERE-PQC-THRESHOLD-01`, and it is the reason a from-genesis follower needs the ceiling + (section 6.4). +- The min-seals floor, the max-seals cap check, and the fail-closed loader (section 3.2). +- Emergency options exist as first-class command-line options `--Xaere-pq-anchor-disarm` and + `--Xaere-pq-anchor-min-seals-max` layered onto the same properties, command line winning + (source: `anchor/app/.../AerePqEmergencyOptions.java`, `PqAnchorConfig.java` javadoc). + +--- + +## 4. Precompiles + +### 4.1 The five mainnet precompiles + +Activated on mainnet at block **9,189,161** (the futureEips milestone, section 1.3) (source: +`README.md`; sources in `precompiles/`, installed by `patches/0001`). + +| Address | Algorithm | Standard | Gas | Input | Output | +|---|---|---|---|---|---| +| `0x0AE1` | Falcon-512 verify | NIST Falcon round 3 | 40,000 flat | `pk(897) \|\| sm` | 32-byte word: `...01` valid, `...00` invalid | +| `0x0AE2` | Falcon-1024 verify | NIST Falcon round 3 | 75,000 flat | `pk(1793) \|\| sm` | same | +| `0x0AE3` | ML-DSA-44 verify | FIPS 204 | 55,000 flat | `pk(1312) \|\| sig(2420) \|\| message` | same | +| `0x0AE4` | SLH-DSA-SHA2-128s verify | FIPS 205 | 350,000 flat | `pk(32) \|\| sig(7856) \|\| message` | same | +| `0x0AE5` | SHAKE256 XOF | FIPS 202 | 60 + 12 per 32-byte word of (input data + output) | 32-byte big-endian `outLen` word, then data | `outLen` bytes | + +(gas and lengths: source: `precompiles/Falcon512PrecompiledContract.java`, +`Falcon1024PrecompiledContract.java`, `MLDSA44PrecompiledContract.java`, +`SLHDSA128sPrecompiledContract.java`, `SHAKE256PrecompiledContract.java`) + +Verification precompiles never revert on malformed input; they return the 32-byte zero word. A +too-short input (size <= pk length for Falcon; size < pk+sig for the fixed-length pair) returns +invalid (source: the `computePrecompile` methods). + +### 4.2 Falcon input framing, exactly (the message sits in the MIDDLE) + +(source: `precompiles/AereFalconSupport.java`; normative, and the single most common integration +mistake) + +``` +input = pk || sm +pk = (0x00 + logn) || packed_h 897 bytes (Falcon-512, logn=9, header 0x09) + 1793 bytes (Falcon-1024, logn=10, header 0x0A) +sm = sigLen(2, big-endian) || nonce(40) || message || esig +esig = (0x20 + logn) || compressedSignature (0x29 for Falcon-512, 0x2A for Falcon-1024) +sigLen == len(esig) +``` + +This is the NIST reference signed-message convention. The message is between the nonce and the +signature, NOT appended after a detached signature; a caller that places the message at the end +gets a clean `0` for a perfectly valid signature and the precompile looks dead. Falcon signatures +are compressed and therefore VARIABLE length (614 and 616 bytes were measured for two signatures +by the same key), so `sigLen` must be read per call, never hard-coded [MEASURED 2026-08-09, +recorded in the repository operating notes; framing itself: source as above]. The precompile +checks the pk header byte, the esig header byte, and the length arithmetic, and answers invalid on +any mismatch. Internally the verifier is Bouncy Castle; no cryptography is reimplemented (source: +`AereFalconSupport.java`). + +For `0x0AE3` and `0x0AE4` the NIST `sm = signature || message` convention applies with the fixed +signature lengths in the table; the remainder of the input after `pk || sig` is the message. + +`0x0AE5` reads a 32-byte big-endian output length (values above 65,536 are capped to 65,536; a +nonzero high 28 bytes also caps; input shorter than 32 bytes returns empty output) and returns the +SHAKE256 XOF of the remaining bytes at that length (source: +`precompiles/SHAKE256PrecompiledContract.java`). + +### 4.3 Testnet-only precompiles: NOT on mainnet + +`0x0AE6` (ML-KEM-768 deterministic encapsulation, FIPS 203, gas 60,000) and `0x0AE7` (Falcon +HashToPoint SHAKE256 rejection sampler, gas 60 + 12/word) exist in `patches/0002` and are testnet +only. A mainnet node must NOT apply patch 0002. The live-chain verification script asserts their +ABSENCE from mainnet as a falsifiable check (source: `README.md`; +`scripts/verify-live-precompiles.mjs`). + +### 4.4 Verification against the live chain + +`node scripts/verify-live-precompiles.mjs` runs 13 checks (NIST KAT/ACVP vectors from `vectors/`, +every signature check paired with a tampered negative control, plus the two absence checks); +13/13 passed against `rpc.aere.network` at block 10,566,696 [MEASURED 2026-07-20, output recorded +in `README.md`]. The BN254 zero-knowledge verifiers used elsewhere in the Aere stack are classical +and Shor-breakable, and are never described as quantum-safe (source: `README.md`, scope boundary). +`0x100` P-256 verify (RIP-7951) is also live and classical; it is upstream Besu, not part of this +fork (source: `README.md`; format not specified here, see section 9, G7). + +--- + +## 5. Fee rules + +### 5.1 EIP-1559 with an Aere floor + +London is active from block 0 (source: `genesis.json`). Upstream Besu's `LondonFeeMarket` carries a +default base-fee floor of 7 wei, which is why the pre-fork steady state of this mostly-empty chain +was ~7 wei (source: `patches/0004-aere-basefee-floor.patch`, visible in the diff context, +`DEFAULT_BASEFEE_FLOOR = Wei.of(7L)`). + +From block **10,141,734** chain 2800 clamps the computed EIP-1559 base fee to +`max(computed, 1_000_000_000)` wei (1 Gwei). The clamp sits on all three return paths of +`computeBaseFee`, which Besu uses for BOTH production and validation, and is gated by the two +system properties (`aere.basefee.floor.forkBlock`, `aere.basefee.floor.value`); with them unset the +patch is a byte-for-byte no-op (source: `patches/0004-aere-basefee-floor.patch`). A block's +`baseFeePerGas` is the real consensused header value; there is no display shim on the public +endpoints since the floor went live. + +### 5.2 What a follower must and must not enforce + +- Besu's QBFT ruleset does not recompute the base fee at import (section 2.5), so a Besu-derived + follower needs the patch only to COMPUTE what the network computes (and to propose correctly if + it ever proposed); it will import the chain either way. That is a finding about a missing + upstream rule, not a licence to skip the patch (source: `RUN-A-NODE.md`, "Which Besu"). +- A client that DOES validate `baseFeePerGas` at import (the second Aere client, Nethermind-based, + does) MUST implement the floor as a VALIDATION rule with one historical exception window, or it + cannot sync: see section 8, D3. Inside the window the header's own `baseFeePerGas` is accepted as + given; outside it the floor is enforced normally, so a NEW violation still stops the client + (source: `aere-client2/src/Nethermind/Nethermind.Core/AereFloorLapse.cs` in the public Aere + repositories; the file is not part of this bundle). +- Why the window cannot be a calculation rule: inside it the base fee is NOT a function of the + parent header. Producers with and without the floor alternated by round winner (block 12,978,617 + is 875,000,000 wei, its child 12,978,618 is 1,000,000,000), so no deterministic recomputation can + reproduce the sequence (source: `AereFloorLapse.cs` javadoc). +- `--min-gas-price` is a local mempool policy, not consensus; the Foundation nodes run it at zero + (source: `RUN-A-NODE.md`). + +--- + +## 6. Sync requirements: what a third-party client must implement to follow the chain + +This section is the checklist. Each item names where its details live. + +### 6.1 Identity and history + +1. Ship `genesis.json` byte for byte (sha256 in section 1.1); verify your block 0 equals + `0xd86d57a8...c5d7f1a`. Ship the configured QBFT transition 2138451 unchanged (section 1.4). +2. Implement the EVM fork schedule of section 1.3, including the Aere futureEips semantics: + Osaka-based ruleset, the five precompiles of section 4, and the EIP-2935 per-block system write, + all from block 9,189,161. A build missing any of these diverges on state root at exactly that + height [MEASURED 2026-08-15, `IMPORT-PROOF-STARE-2026-08-15.md`]. +3. Implement the base-fee floor from 10,141,734 (section 5), with the D3 window if you validate + base fees. +4. Implement QBFT header validation with the Aere extraData codec exactly as in sections 2.3 and + 2.4 (strict leave, canonical round trip, optional sixth element), the constant difficulty 1, and + the hash pre-image that excludes round, ECDSA seals and the Falcon certificate. +5. Implement the anchor rules of section 3 from height 13,014,000, with the registries of + section 3.5 and the historical windows of section 8 (D4, D5). Without the windows a from-genesis + sync stops at 13,267,824 and cannot proceed; without the parent-height registry alignment it + stops at 13,014,000. + +### 6.2 Sync mode + +Full sync from block 0 is the mode that verifies the published history, and it is what the anchor +requires: an armed node refuses any lighter mode at startup (section 3.2). Snap sync against a QBFT +chain trusts a peer for the state and verifies far less (source: `RUN-A-NODE.md`). The Foundation +fleet runs BONSAI storage; storage format is a local choice, not consensus. + +### 6.3 Peering + +Discovery needs a bootnode enode. Bootnodes are deliberately not published in this bundle; a +current enode is provided by mail (office@aere.network, subject "bootnode") until a dedicated +public bootnode exists (source: `RUN-A-NODE.md`). Both discovery UDP and p2p TCP must be open in +both directions. The bootnodes are full nodes outside the validator set, Foundation-operated, and +their liveness at any moment is only provable by your own `net_peerCount` (source: `RUN-A-NODE.md`). + +### 6.4 The genesis-set threshold problem, and the published answer + +A node syncing from block 0 starts INTO the genesis validator set (N=3, quorum 2). The anchor +threshold guard (section 3.7) refuses to arm K=3 or K=6 against that set, so a from-genesis +follower with the published schedule cannot start at all without one more setting. The published +answer is `aere.pq.anchor.minSealsCeiling=1`, which lowers the EFFECTIVE seal-count floor to +`quorum(3) - 1 = 1` WITHOUT editing the schedule (source: `RUN-A-NODE.md`, "Why the seal-threshold +ceiling"). + +State the consequence honestly, because it is a real weakening of one check on the follower: with +the ceiling at 1, the follower's seal-COUNT requirement is 1 at every height, not 3 or 6. What is +NOT weakened: every anchor must still carry a digest that binds its certificate under the block +hash, indices must still be strictly increasing, every carried seal is still Falcon-verified +against the height-correct registry, and eligibility in the parent's validator set is still +enforced. Real anchor blocks carry 3 to 9 seals, so all of history imports cleanly. The seal-count +floor is a proposer-liveness property relative to the current set, not a history-verification +property; a validator, which starts into the full set, does not set the ceiling (source: +`RUN-A-NODE.md`). An implementation that instead makes its threshold guard aware of the validator +set it will have at the evaluated HEIGHT (rather than at startup) can enforce the full schedule +end to end; the published Besu-based package does not do this today [NOT MEASURED: no such +implementation exists to compare against]. + +### 6.5 Facts a syncing client will observe, so they are expectations rather than surprises + +- Blocks are almost all empty; the chain advances ~2 blocks/second after the rate change. +- Between anchor heights, headers carry no Falcon element (extraData 634 bytes at N=9); at anchor + heights they carry element 5 and a digest in vanityData (2,516+ bytes) [MEASURED 2026-08-09 on + the live chain; recorded in the repository operating notes; sizes are descriptive, not + normative]. +- Stored extraData for the same block can differ between peers (section 2.3); only the hash + pre-image is canonical. +- The full from-genesis import of exactly this bundle's patches reached the live tip (height + 14,088,211) and followed it, with zero anchor rejections and zero state mismatches, after the two + historical windows and the H boundary alignment were carried; the consensus test suites stood at + 641 tests, 0 failures, with negative controls run in all four directions on the windows + [MEASURED 2026-08-16, run and controls recorded in `IMPORT-PROOF-STARE-2026-08-15.md`]. +- The EIP-4788 (Cancun) beacon-roots system call targets a contract with no code on chain 2800, so + it is a per-block no-op that Besu reports at ERROR level in its log; state is unaffected. A + client must not treat the empty beacon-roots contract as an error in its own sync + [MEASURED 2026-08-07, finding D-153 in the repository's findings register; not restated in any + bundle file, see section 9, G8]. + +--- + +## 7. Formal models: what is proven, where, and the limits + +The machine-checked models live in `../aere-research/formal-consensus/` (SMT, z3; runner +`run_consensus_verification.py`) and, for TLA+/TLC, in the repository directory `formal-tla/` +(not part of this bundle). Method rule throughout: no result counts as a proof unless its negative +control was RUN and produced the predicted violation (SAT model or TLC trace) (source: +`../aere-research/formal-consensus/RULEAZA-TOT.md`). + +Suite status [MEASURED 2026-08-24, `python run_consensus_verification.py`, Windows, z3 4.16.0]: +35 models, 35 pass (14 consensus + 11 contract + 10 application), 282 PROVED + 149 CEX-FOUND, +0 FAILED, ~39 s wall (source: `RULEAZA-TOT.md`). + +Consensus properties proven, with tool and scale (source: `RULEAZA-TOT.md` and the repository +document `strategie/VERIFICARE-FORMALA-2026-08-24.md`): + +| Property | Tool / N | Model | +|---|---|---| +| Agreement (no two different blocks finalize at one height) | SMT parametric N=4..16 plus UNBOUNDED quorum-intersection arithmetic (2q > N for all N); TLC at N=4 | `qbft_safety_smt.py`, `QBFT.tla` | +| Quorum threshold is exactly ceil(2N/3) (N=9 gives 6) | SMT unbounded | `qbft_safety_smt.py` (L1), `pqfinality_smt.py` (Q0) | +| Equivocation: <= f Byzantine double-signers cannot break agreement | SMT N=4..16; TLC | `qbft_safety_smt.py` (L2), `QBFT.tla` | +| Round-change locking: a view change cannot finalize a different value | SMT N=4..13; TLC N=4 | `qbft_locking_smt.py`, `qbft_safety_smt.py` (L3) | +| Liveness post-GST: within f+1 rounds an honest proposer commits | SMT N=4..7 | `qbft_liveness_smt.py` | +| Network partitions: no split-brain in any split; the minority side cannot commit; progress returns on heal | SMT parametric N=9 + unbounded; TLC N=4 EXHAUSTIVE (6 configs, 0 states left in queue) | `partition_safety_smt.py`, `QBFTPartition.tla` | +| PQ anchor certificate acceptance is fail-closed (quorum, root, size, domain, rotation gates each individually load-bearing) | SMT N=9 | `pqfinality_smt.py`, `anchor_blocking_quorum_smt.py` | +| A round change on an anchor height cannot yield two different valid certificates; uniqueness comes from binding to the FINALIZED block, not from K (K=3 alone admits two, shown SAT) | SMT N=9 | `anchor_viewchange_smt.py` | +| Emergency ceiling only lowers K | SMT | `pqanchor_ceiling_monotonicity_smt.py` | +| Registry rotation covers every height exactly once; index binding | SMT | `registry_rotation_coverage_smt.py`, `registry_index_binding_smt.py` | + +The honest boundary, stated as prominently as the results (source: `RULEAZA-TOT.md`, "Granita +cinstita"; `VERIFICARE-FORMALA-2026-08-24.md`): + +- Model checking is not a complete proof. TLC exhausts only the stated finite configuration + (partitions at N=4, single height). The full `QBFT.tla` model does NOT terminate in local + budgets; what exists there is a partial explicit-state witness (9.35 million states, no + violation, N=4 round 0), not a verification. +- Parametric SMT proves the property for THAT N (9), plus unbounded arithmetic for the + quorum-intersection core. No mechanized induction over all executions at arbitrary N + (Coq/Isabelle/TLAPS) exists. +- The models verify the DESIGN's combinatorics, not the Java bytecode or the EVM. The design-to- + binary gap has historically been caught only by importing real blocks, never by a form check. +- Cryptographic primitives (keccak injectivity, Falcon verification soundness) are modeled as + trusted, not re-proved. +- Liveness results assume partial synchrony (post-GST); asynchronous liveness is impossible (FLP) + and is not claimed. +- Nothing in the formal results implies "post-quantum consensus" per block; the anchor is additive + (section 3.1). + +--- + +## 8. Known divergences, historical windows, and corrections. Normative. + +Every entry here is something a naive implementation gets wrong. Each carries its dates and its +ground. D3, D4 and D5 are MANDATORY for a from-genesis sync; the rest are mandatory knowledge. + +**D1. The sub-second transition: configured 2138451, measured 2,137,652.** Ship the configured +value; report the measured one. Full statement in section 1.4. Both numbers appear in public Aere +material historically; the wrong lesson is to pick one and "fix" the other (source: +`RUN-A-NODE.md`; measurement dated there). + +**D2. Genesis `qbft.startBlock` folklore.** Any Aere document giving 2,138,451 as "when the chain +accelerated" is quoting configuration, not measurement; the acceleration is at 2,137,652 +[MEASURED, section 1.4]. The genesis file the fleet runs is identical on all nine validators +(sha256 `361709dcce...`) and is not to be touched. + +**D3. Base-fee floor lapse, blocks 12,978,617 to 13,087,959 inclusive.** On 2026-08-09 the +validators lost the floor system properties for about sixteen hours (a configuration rewrite +dropped them); producers with and without the floor alternated. Inside this range a validating +client accepts the header's `baseFeePerGas` as given; outside it, the floor holds from 10,141,734. +Bounds were established by exhaustive scan after a binary search returned a wrong answer +(12,978,949; the violation predicate is intermittent, hence non-monotone), and the true lower bound +was found by the second client refusing to sync past 12,978,617. The 500 blocks before the window +and the 2,000 blocks after it contain zero violations (source: +`aere-client2/src/Nethermind/Nethermind.Core/AereFloorLapse.cs`, public Aere repositories; +measurements dated 2026-08-09..11 in that file's javadoc). Besu-derived followers import this range +regardless (section 5.2); clients that validate base fees need the window. + +**D4. Anchor disarmed window, blocks 13,267,824 to 13,268,944 inclusive: 36 anchor heights, +relaxation EVERYTHING.** On 2026-08-10 all nine validators restarted within minutes; no node held +seals, the next anchor height could not be proposed, and the chain stopped for nine minutes. The +fleet-wide emergency measure switched the anchor rules off; the canonical headers produced +meanwhile carry the client version string in vanityData (no digest) although a certificate is +attached, and their certificate indices are in arrival order rather than sorted. Inside this window +a validating client accepts the header as written, EXCEPT that the certificate must still decode +and must still carry distinct non-negative indices (distinctness is the anti-inflation property and +is never relaxed). Bounds are fixed in code so the window cannot widen at runtime, and producers +never consult it, so it is closed by construction. Bounds were measured by decoding every anchor +height from H to 14,077,000 one by one (25,252 heights), not searched (source: +`anchor/consensus/common/.../PqAnchorLapse.java`, first window; +`IMPORT-PROOF-STARE-2026-08-15.md`). + +**D5. Anchor threshold-lowered window, blocks 13,268,976 to 13,890,544 inclusive: 19,425 anchor +heights, relaxation THRESHOLD_ONLY, floor 1.** After the D4 incident the rules were restored with +the emergency seal ceiling lowered (raising the threshold back in one step had stopped the chain a +second time). Every header in this range binds its digest and carries sorted indices; 2,926 of the +19,425 anchor heights carry fewer seals than the schedule asks, the fewest being 1. Inside the +window the required count is `min(schedule, 1)`; nothing else is relaxed, and every carried seal is +still verified. The measured floor is 1, never 0: an empty certificate is NOT accepted where one +was required (source: `PqAnchorLapse.java`, second window). This window is also why the schedule +step `13034000:3` reads "enforced since 2026-08-14" in prose: the step was configured earlier, but +the effective minimum was held at 1 by the ceiling until the window's end (source: `anchor/README.md`; +`PqAnchorLapse.java`). + +**D6. Correction of 2026-08-19 (repository finding D-235): `aere.falcon.forkBlock=14050000` armed a +RETIRED rule and changed no enforcement.** The fleet was configured on 2026-08-15 with that value, +intended as a per-block blocking Falcon quorum, and public material said so. In the shipped code +the rule it arms (`FalconSealValidationRule`) retires at the anchor activation height 13,014,000 in +favour of the anchor rules (`PqAnchorConfig.legacyFalconRuleRetirementBlock`), so at 14,050,000 it +was already inert. The chain itself confirms it: blocks between anchors carry no Falcon seals, +which a per-block rule would have rejected. The earlier public statement that from 14,050,000 every +block required a 2f+1 Falcon quorum to finalize is WITHDRAWN; nothing changed at that height +(source: `anchor/README.md`, correction paragraph; `RUN-A-NODE.md`, correction paragraph; +code: `FalconSealValidationRule.java`, `PqAnchorConfig.java`). A follower leaves +`aere.falcon.forkBlock` unset; set or unset, it gates nothing above H. + +**D7. Genesis `alloc` comment strings are wrong and published anyway.** The dash characters are +mangled (a historical encoding accident) and the 1,400,000,000 AERE account is labelled "Mining +Reserve" although chain 2800 has no mining and never has (it is the staking and emissions reserve). +No client reads these strings; they stay because the file is published byte for byte as the fleet +holds it (source: `RUN-A-NODE.md`). + +**D8. The H boundary registry alignment (one block, exactly).** The seals rule resolves the +registry at the PARENT's height; the registry schedule binds inclusively from H; therefore at the +first anchor the lookup at H-1 must be answered by the registry bound at exactly H, and the +boundary is exactly one block wide (two or more blocks below the schedule changes nothing). The +first public build lacked this and rejected the first real anchor with "certificate carries index +0, which the registry does not bind"; the fix carries a dedicated test with a run negative control +(source: `IMPORT-PROOF-STARE-2026-08-15.md`; the test `PqParentHeightAlignmentTest` in `anchor/`). + +**D9. K schedule steps and their real dates.** `13014000:0` (warm-up; the first anchor's +certificate is legitimately empty), `13034000:3` (f+1 of nine; effectively in force from +2026-08-14, see D5), `14961456:6` (2f+1, in force from 2026-08-21) (source: `RUN-A-NODE.md`, +`anchor/README.md`). "Certificate of f+1 validators" and "quorum certificate" are different +claims; before 14,961,456 only the former is true, and calling K=3 a quorum is a counting error +anyone will catch (source: `anchor/README.md`, "Terminology, used precisely"). + +**D10. Anchors described as "universal since block 13,889,296".** Both `RUN-A-NODE.md` and +`anchor/README.md` carry this phrase for the certificate. Its precise operational definition (what +exactly changed at that height, as distinct from the D5 window ending at 13,890,544 and the +on-chain registry pin at 13,889,290) is not stated in any bundle file [NOT MEASURED; flagged in +section 9, G5]. Implementations need no rule keyed to 13,889,296; the windows of D4/D5 plus the +schedule are sufficient to sync [MEASURED 2026-08-16, the full import of +`IMPORT-PROOF-STARE-2026-08-15.md` used exactly those]. + +**D11. The Cellframe counterexample governs how the anchor may be described.** "No public chain has +a block hash covering a post-quantum validator certificate" is FALSE as stated: Cellframe's ESBoCS +signs block candidates with keys resolving to Dilithium/Falcon/SPHINCS+ and hashes the block with +signatures attached. The defensible sentence is: no public chain has a post-quantum validator +certificate under the block hash that is PROVEN AND INDEPENDENTLY VERIFIABLE, and this bundle is +the "proven and verifiable" half of that sentence (source: `anchor/README.md`, "One claim we +retracted, on purpose"). + +**D12. Validator-set size statements are dated.** Seven validators until 2026-08-09, nine since +(source: `RUN-A-NODE.md`). Registry rotation heights (13,014,000 seven keys; 13,600,000 nine keys) +are the authoritative in-protocol trace of the expansion (source: `registries/README.md`). + +--- + +## 9. What this specification cannot state from the published sources + +Honest list, for completion in a later revision. An implementer hitting one of these should treat +the gap as real rather than assume. + +- **G1. Bootnode enodes.** Deliberately unpublished; obtainable by mail (section 6.3). A + third-party implementation cannot discover peers from this bundle alone. +- **G2. The upstream QBFT hashing and commit-seal plumbing.** `BftBlockHashing`, + `BftBlockHeaderFunctions`, `BlockHeaderValidator` and `HeaderValidationMode` are upstream files + not carried in the overlay; the hash pre-image statement in section 2.3 is supported by the codec + comments, by the encoding types, and by byte-exact reproduction of live block hashes on two real + headers, but the upstream call sites themselves must be read at commit `d2032017` to be cited + line by line. Similarly, which validation MODE each entry path (gossip NewBlock, full-sync + import, header download) applies has been proven on the import path only (source: the NEMASURAT + list in the repository document `consensus-pqc/CE-DEVINE-INVALID-LA-ANCORA-2026-08-07.md`). +- **G3. The exact byte values of the QBFT vote add/drop constants.** Defined in upstream `Vote` + (referenced by the codec); not restated here rather than quoted from memory. +- **G4. Per-block gas accounting beyond the precompiles.** The chain follows the Osaka gas + schedule of upstream Besu at `d2032017`; this bundle does not enumerate it, and neither does this + document. +- **G5. The precise definition of "universal since 13,889,296"** (D10), and the interface of the + on-chain registry anchor contract at `0x5214F0e53899Dad805ef3AdF0624F5e49d2a5EB5` (section 3.5): + the follower configuration names it, but its ABI, storage layout and exact role at + block 13,889,290 are not specified in any bundle file. +- **G6. The validator (seal-production) configuration.** `aere.falcon.key`, + `aere.falcon.attachBlock`, `aere.falcon.attach*`, `aere.falcon.validatorCount` drive seal + production only and are deliberately outside this follower specification; their live values on + the fleet are not published (source: `RUN-A-NODE.md` names them; nothing publishes values). +- **G7. The `0x100` P-256 (RIP-7951) input/output format.** Live and classical, upstream Besu; + not specified in this bundle. +- **G8. The EIP-4788 no-code observation** (section 6.5) rests on a dated repository finding + (D-153, 2026-08-07), not on a bundle file; a bundle-resident statement (or a fixture proving the + no-op) does not exist yet. +- **G9. Whether a certificate reused by an adversary for its one permitted fork block passes + eligibility in every corner case** was argued from code and never executed as a run (source: + `CE-DEVINE-INVALID-LA-ANCORA-2026-08-07.md`, NEMASURAT item 8). +- **G10. Anything about the chain after 2026-08-25.** Every measured statement carries its date; + the live schedule, set size, and head move. The registries directory and the follower + configuration are the values current at publication. + +--- + +*End of specification. Corrections to this document are made by editing it in the repository and +republishing the bundle; the underlying files it cites are never edited to match the document.*