1126 lines
74 KiB
Markdown
1126 lines
74 KiB
Markdown
# Aere Network chain 2800: full protocol specification
|
|
|
|
Version: 1.2.0, written 2026-08-25, amended 2026-09-02 (section 2.6, D8, G11) and 2026-09-05 (sections 2.6 and 3.1: the message-layer arming heights, the v2 hybrid certificate, the anchor interval schedule). 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 | `e5038e6a1437bd85fa51294609edf2126a997f14f4fa7a65a0d39bd0aa24d2e5` | (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 ten validators since 2026-09-11 (nine from 2026-08-09, seven until then) (source: `RUN-A-NODE.md`, "What the
|
|
bootnode is"). 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, 7 at N=10. 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=10 (since 2026-09-11) is f=3; it was f=2 at N=9.
|
|
|
|
### 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.
|
|
|
|
### 2.6 Consensus messages under post-quantum seal (emission live since 2026-09-02; enforcement armed by height on 2026-09-05)
|
|
|
|
Since 28-31 August 2026 every one of the four QBFT message types (PROPOSAL, PREPARE, COMMIT,
|
|
ROUND-CHANGE) can carry a Falcon-512 seal of its author, and since 2 September 2026 all
|
|
validators of chain 2800 (nine then, ten since 2026-09-11) EMIT such seals on every message they send (source:
|
|
`PqAnchor.java`, the four `*_DOMAIN` constants and `*Message` builders; the four payload classes
|
|
`ProposalPayload.java`, `PreparePayload.java`, `CommitPayload.java`, `RoundChangePayload.java`;
|
|
fleet configuration measured 2026-09-02). This section specifies the wire format, the signed
|
|
pre-images, the emission gates and the enforcement rules exactly as the shipped client implements
|
|
them. **Enforcement is a per-node height switch.** On chain 2800 the four heights were set on all
|
|
nine validators on 2026-09-05 [MEASURED 2026-09-05, `docker inspect` of every validator]:
|
|
`aere.pq.commitPq.forkBlock=17250000`, `aere.pq.proposalPq.forkBlock=17400000`,
|
|
`aere.pq.roundChangePq.forkBlock=17550000`, `aere.pq.preparePq.forkBlock=17700000`. Below each
|
|
height a message of that type without a seal, or with an invalid seal, still counts as a vote; from
|
|
it, it does not. Emission heights on 2800: `proposalPq.attachBlock=16620000`,
|
|
`roundChangePq.attachBlock=16660000`, `preparePq.attachBlock=16690000` (COMMIT seals are emitted
|
|
from the anchor). On the public testnet 28001 all four enforcements have been active since
|
|
2 September 2026 (source: `testnet-public/CITESTE-MA.md`).
|
|
|
|
**Wire format.** Each payload keeps its upstream RLP list and appends, as its LAST element, an
|
|
optional two-element list `[validatorIndex, signature]` (`validatorIndex` an integer index into the
|
|
active Falcon registry, `signature` the Falcon-512 signature bytes). The element is OMITTED when
|
|
there is no seal; an explicit empty list is non-canonical. `writeTo` DEFINES the canonical encoding
|
|
and `readFrom` refuses any encoding that does not reproduce the received bytes, so a message with
|
|
a seal and the same message without one never share a hash (source: `PreparePayload.writeTo`,
|
|
comment "This method DEFINES the canonical encoding"). A COMMIT may additionally carry a list of
|
|
non-Falcon "extra" seals for the hybrid certificate (`[schemeWireId, index, signature]`), REQUIRED
|
|
to be accompanied by a Falcon seal. Since block 17,047,568 (`aere.pq.hybrid.attachBlock`) the
|
|
COMMIT of an anchor's PARENT carries one SLH-DSA-SHA2-128s extra seal per validator; other commits
|
|
carry none, because that seal costs seconds and is used only in the next anchor's certificate
|
|
[MEASURED 2026-09-05] (source: `CommitPayload.java`; `HybridSealProducer.java`).
|
|
|
|
**Signed pre-images: one domain per message type.** Every seal signs
|
|
`keccak256(RLP[DOMAIN, chainId, height, ...])` with an ASCII domain string that names the message
|
|
type, so a seal given honestly for one message type cannot be pasted onto another (source:
|
|
`PqAnchor.java`):
|
|
|
|
| message | domain | pre-image fields after the domain |
|
|
|---|---|---|
|
|
| PROPOSAL | `AERE-PQ-PROPOSAL-1` | chainId, height, round, proposed block digest |
|
|
| PREPARE | `AERE-PQ-PREPARE-1` | chainId, height, round, digest |
|
|
| ROUND-CHANGE | `AERE-PQ-ROUNDCHANGE-1` | chainId, height, target round, hasPrepared, prepared round, prepared digest |
|
|
| COMMIT | `AERE-PQ-COMMIT-1` or the committed-seal hash | see below |
|
|
|
|
The COMMIT seal is special, and the rule was corrected on 2026-09-02 (D8 in section 8): the
|
|
message a commit seal signs is decided in ONE place, `PqAnchorProducer.commitSealMessage`, and is
|
|
**the anchor form** `keccak256(RLP["AERE-PQ-COMMIT-1", chainId, height, round-independent block
|
|
hash])` whenever the anchor is armed at that height (`height + 1 >= aere.pq.anchorBlock`, which on
|
|
2800 is every height since 13,013,999), and the ECDSA committed-seal hash otherwise (source:
|
|
`PqAnchorProducer.commitSealMessage`, `QbftRound.pqSealMessageFor`,
|
|
`MessageValidator.SubsequentMessageValidator`). The anchor form is what lets the same seals be
|
|
carried into the next anchor header's certificate (section 3.3); the round-independent hash is the
|
|
on-chain hash with the round forced to 0 in the committed-seal encoding.
|
|
|
|
**Emission gates (producer side, per node).** `aere.falcon.attachBlock` arms Falcon seals on
|
|
COMMIT messages (on 2800: 13,889,296); `aere.pq.proposalPq.attachBlock`,
|
|
`aere.pq.roundChangePq.attachBlock` and `aere.pq.preparePq.attachBlock` arm the other three (on
|
|
2800: 16,620,000, 16,660,000, 16,690,000, measured 9 of 9 emitting). Absence of a property means
|
|
never. A node without a Falcon key emits nothing and is byte-for-byte upstream on the wire
|
|
(source: `FalconSealSupport.java`, `*_ATTACH_PROPERTY`; fleet options measured 2026-09-02).
|
|
|
|
**Enforcement (validator side, per node): `aere.pq.commitPq.forkBlock`,
|
|
`aere.pq.proposalPq.forkBlock`, `aere.pq.roundChangePq.forkBlock`, `aere.pq.preparePq.forkBlock`.**
|
|
From the configured height a message of that type COUNTS as a vote only if all of the following
|
|
hold; otherwise the message is logged and ignored ("the vote does not count"), it is not a
|
|
consensus fault of the sender and it does not disconnect the peer (source: `PqCommitEnforcement`,
|
|
`PqPrepareEnforcement`, `PqProposalEnforcement`, `PqRoundChangeEnforcement`, the `refusal`
|
|
methods and their message strings):
|
|
|
|
1. the message carries a seal;
|
|
2. the registry active at that height binds the seal's index to exactly the message's ECDSA
|
|
author (recovered from the upstream signature): the seal is bound to the author, so a seal
|
|
from validator A cannot be attached to validator B's message;
|
|
3. the registry accepts the index (it refuses indices outside the active set);
|
|
4. the signature verifies, with the Falcon-512 public key of that index, over the pre-image of
|
|
the table above (for COMMIT, over the message `commitSealMessage` names at that height);
|
|
5. for a COMMIT that carries extra seals, every scheme the hybrid schedule requires at that
|
|
height is present, of the same index, and verifies over the SAME message.
|
|
|
|
Below the configured height nothing changes: the upstream ECDSA rules alone decide. There is no
|
|
consensus binding on these heights (the same caveat as section 3.2): two validators armed at
|
|
different heights disagree about which votes count, and the published activation procedure arms
|
|
every validator at the same four heights, one node at a time, with the chain measured to advance between
|
|
restarts (source: `ARMAREA-IMPUNERII-MESAJELOR-2026-09-02.md`,
|
|
`armeaza-impunerea-pe-flota.sh`).
|
|
|
|
**Order of activation, and why.** The published package arms COMMIT first (a commit that does not
|
|
count only delays finality within the round; the liveness margin is measured), then PROPOSAL,
|
|
then ROUND-CHANGE (whose enforcement touches only the failure path: the happy path never emits a
|
|
round change), and PREPARE last (the strictest: without a quorum of sealed PREPAREs a round
|
|
cannot reach "prepared"). Rehearsed end to end on the seven-node kit (F95: the four heights in
|
|
one restart, a coordinated postponement, each height crossed, a muted node, a sub-quorum stop and
|
|
its healing) and, since 2026-09-02, WITH the anchor armed as on 2800 (source:
|
|
`consensus-pqc/fullpq-activare-2026-08-28/`, `F-lib.sh`, `dovezi/`). The machine-checked model of
|
|
these rules is `aere-research/formal-consensus/pq_message_enforcement_smt.py` (section 7).
|
|
|
|
**What this section does not claim.** Node-to-node authentication (RLPx) is ECDSA. The hybrid
|
|
(Falcon + SLH-DSA) certificate is implemented and tested but no hybrid keys exist on 2800. None of
|
|
this is audited by a third party. Until the four `forkBlock` heights are set on the fleet, chain
|
|
2800's consensus is post-quantum in EMISSION only, and the defensible sentence remains the one in
|
|
section 3.1.
|
|
|
|
---
|
|
|
|
## 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"), and from block
|
|
17,225,968 every 128th (`aere.pq.anchorIntervalSchedule=17225968:128`, set on every node on
|
|
2026-09-05), carries a certificate of validator seals over its parent, and the anchor block's keccak
|
|
block hash covers a 32-byte commitment to that certificate. Until block 17,047,600 the certificate is
|
|
v1, a list of `[index, falconSignature]` pairs under the domain `AERE-PQ-ANCHOR-1`; from block
|
|
17,047,600 (`aere.pq.anchorV2Block`) it is v2, `RLP[2, [[scheme, index, signature], ...]]` under the
|
|
domain `AERE-PQ-ANCHOR-2`, digest `keccak256(RLP[domain, chainId, parentNumber, parentHash,
|
|
BYTES(rlp(certificate))])`, with scheme 1 = Falcon-512 and 2 = SLH-DSA-SHA2-128s; the scheme
|
|
schedule `aere.pq.schemeSchedule=17047568:falcon-512+slh-dsa-sha2-128s` names which schemes are
|
|
required, the minimum applies to each scheme separately, and every SLH-DSA seal must sit at an index
|
|
that also carries a valid Falcon seal [MEASURED 2026-09-05, 400 consecutive anchors] (source:
|
|
`anchor/consensus/common/.../PqAnchorV2.java`, `PqSchemeSchedule.java`; the public verifier
|
|
`tools/verify-anchor.mjs`). 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 the nine validators of that time; enforced on the fleet since
|
|
2026-08-14), and **6 from block 14,961,456** (of nine then, a full 2f+1 quorum at that size and below the ten-validator quorum of seven since 2026-09-11, 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 valid Falcon-512 validator seals (of ten validators since 2026-09-11) 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.falcon.anchor.address=0xC01Bb2843EFAF92D8389F14aC1dD26dbeAf9144C
|
|
-Daere.falcon.anchor.block=13889290
|
|
-Daere.falcon.attachBlock=13889296
|
|
-Daere.falcon.attachInterval=32
|
|
-Daere.falcon.forkBlock=14050000
|
|
-Daere.falcon.manifest=<registries>/manifest-18082816.json
|
|
-Daere.falcon.minAttachFutureMargin=5
|
|
-Daere.falcon.registry.history=<registries>/manifest-13014000.json,<registries>/registru-2800-v2-13600000.properties
|
|
-Daere.falcon.validatorCount=10
|
|
-Daere.pq.anchorBlock=13014000
|
|
-Daere.pq.anchorInterval=32
|
|
-Daere.pq.anchorIntervalSchedule=17225968:128
|
|
-Daere.pq.anchor.maxSeals=9
|
|
-Daere.pq.anchorMinSeals=13014000:0,13034000:3,14961456:6,17102384:0,17102416:6
|
|
-Daere.pq.anchorV2Block=17047600
|
|
-Daere.pq.chainId=2800
|
|
-Daere.pq.hybridRegistry=<registries>/hibrid-10.properties
|
|
-Daere.pq.schemeSchedule=17047568:falcon-512+slh-dsa-sha2-128s
|
|
-Daere.pq.anchor.minSealsCeiling=1
|
|
```
|
|
|
|
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.anchorIntervalSchedule` (`h:interval,...`): from `h` the interval changes; `h` must itself
|
|
be on the previous grid and the new interval a multiple of the old one, so every new anchor height is
|
|
also an old one. On 2800: `17225968:128`, so from that block `(n - 13014000) % 128 == 0`. A node
|
|
without the schedule keeps judging the old grid and rejects the first old-grid height that carries
|
|
no certificate; every node that judges anchors must carry the same schedule [MEASURED 2026-09-05 on
|
|
the public testnet, schedule `312000:128`].
|
|
- `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 until 2026-09-10; kept for provenance, not wired (its nine keys are the ones in `registru-2800-v2-13600000.properties`) | `23736b7d4ba1d3213496f8b5a5f756d5c958b08efebdeb1329b24d7dfa45fb6e` |
|
|
| `manifest-18082816.json` | 18,082,816 | 10 | head registry since 2026-09-10, passed as `aere.falcon.manifest`; the nine validators and one further validator index bound at 18,082,816 | `c6601ae5159875a090066946991517c9a38e5d6206da76b018c0917e1ca6f12f` |
|
|
| `hibrid-10.properties` | 18,082,816 | 10 | the SLH-DSA-SHA2-128s public keys for the same ten indices (`formatVersion=hybrid-1`), passed as `aere.pq.hybridRegistry` since 2026-09-10 (`hibrid-1.properties` before) | `35d69f60e357d405aff39fd33858de901dfb21327b0c4aeb18be17ffdaa115d6` |
|
|
|
|
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).
|
|
|
|
**Domain tags of the registry binding and of node-local persistence (named here so that a
|
|
third-party client sees every `AERE-PQ-*` tag the shipped client uses).** A registry row binds a
|
|
Falcon key to a validator address with two signatures over two distinct pre-images (source:
|
|
`PqRegistryBinding.java`, class javadoc "The canonical pre-image"):
|
|
|
|
- the **possession proof** is a Falcon-512 signature over
|
|
`"AERE-PQ-POP-1" || uint8(formatVersion) || uint64be(chainId) || uint64be(bindHeight) ||
|
|
uint32be(count) || uint32be(index) || address(20) || uint32be(len(publicKey)) || publicKey`.
|
|
The tag sits at offset 0 INSIDE the hashed pre-image, so a possession proof can never be
|
|
replayed as a seal (the seal pre-images are `AERE-PQ-COMMIT-1` and the section 2.6 domains);
|
|
the public key is inside the signed message because Falcon does not have exclusive ownership;
|
|
- the **claim** is an ECDSA signature by the validator's consensus key, in an EIP-191 context
|
|
that mixes the tag `AERE-PQ-CLAIM-1`, so a key cannot be moved under another validator's
|
|
address, indices cannot be swapped, and a key cannot appear under an address whose owner did
|
|
not ask for it.
|
|
- `AERE-PQ-SEALSTORE-1` is the label of the node-local file in which a node persists the seals it
|
|
has heard for its chain head across restarts (`aere-pq-seals.rlp`); it is not consensus data and
|
|
a client is free to implement it differently or not at all (source: `PqSealStore.java`).
|
|
|
|
### 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, 7 at N=10) (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 Besu validators (the tenth validator, Nethermind since 2026-09-11, runs the same chain from a chainspec derived from it)
|
|
(sha256 `e5038e6a14...` since 2026-09-10, when a third `config.pqRegistryHash` entry at 18,082,816 was added; `361709dcce...` before) 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 of nine at the time; below the ten-validator quorum of seven since 2026-09-11; 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 from then, ten since 2026-09-11
|
|
(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`).
|
|
|
|
---
|
|
|
|
**D8. The COMMIT seal message under an armed anchor (found 2026-09-02, D-311).** A verifier of the
|
|
COMMIT enforcement that checks the seal over the ECDSA committed-seal hash refuses EVERY commit as
|
|
soon as the anchor is armed, because the emitter signs the anchor form (section 2.6). The shipped
|
|
client before 2026-09-02 had exactly this divergence between its emitter and its verifier; it was
|
|
found on the public testnet 28001 at the first enforcement height (the chain stopped at block
|
|
999), fixed by deriving both sides from one function, proven with unit and plumbing tests, a
|
|
rehearsal with the anchor armed, and the testnet itself, and the fleet was moved to the fixed
|
|
build on 2026-09-02 (image `d311-20260902`, version `26.4.0-aere.1-d203201-wt95fac2d7`). A
|
|
third-party client implementing COMMIT enforcement MUST verify over the anchor form whenever
|
|
`height + 1 >= aere.pq.anchorBlock` (source: `testnet-public/D-311-DOVADA-2026-09-02.md`,
|
|
`deploy/validators/d311-2026-09-02/SURSA.md`).
|
|
|
|
**D9. Osaka header fields under QBFT (found 2026-09-02, D-322).** On an AERE QBFT chain with the Osaka
|
|
fork active from genesis (the public testnet 28001: `osakaTime=0`, `futureEipsTime=0`), Besu writes the
|
|
EIP-7928 block access list hash (`balHash`) and the EIP-7843 `slotNumber` ONLY in block 0; every later block
|
|
omits both fields (measured on blocks 1 and 47744 through `eth_getBlockByNumber`). A second client that
|
|
requires them on every header, or that stamps a computed access list hash onto the processed header,
|
|
diverges at block 1. The Nethermind client accepts the chain's form after genesis and keeps validating the
|
|
fields wherever a header carries them (source: `nethermind-pqc/nethermind-intree/patches/osaka-antet-besu.sh`
|
|
in the operator repository; gate `scripts/deschise/antetul-osaka-e-al-lantului.sh`). Chain 2800 has not
|
|
reached Osaka; this divergence will apply to it on the same terms when it does.
|
|
|
|
## 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).
|
|
- **G11. When, and whether, the four message-layer enforcements of section 2.6 are armed on
|
|
2800.** At publication they are not; the heights are a founder decision. A reader must measure
|
|
the fleet's `aere.pq.*Pq.forkBlock` properties (or observe refusals in node logs) rather than
|
|
assume either state.
|
|
- **G10. Anything about the chain after 2026-09-02.** 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.*
|