diff --git a/SPEC.md b/SPEC.md index e7330a6..57f4e6f 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,6 +1,6 @@ # 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 +Version: 1.1.0, written 2026-08-25, amended 2026-09-02 (section 2.6, D8, G11). 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. @@ -264,6 +264,100 @@ factory and grepping `consensus/`; recorded in `RUN-A-NODE.md`, "Which Besu", an 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 not yet armed) + +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 nine +validators of chain 2800 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 and is NOT active on chain 2800 at publication:** +a message without a seal, or with an invalid seal, still counts as a vote everywhere on 2800. On +the public testnet 28001 all four enforcements are active (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; no extra seals exist on the fleet today (source: +`CommitPayload.java`, constructor javadoc "empty for every commit on the fleet today"). + +**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 +all nine 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 `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 @@ -505,6 +599,25 @@ contract at `0x5214F0e53899Dad805ef3AdF0624F5e49d2a5EB5` from block 13,889,290 ( `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) @@ -905,6 +1018,18 @@ are the authoritative in-protocol trace of the expansion (source: `registries/RE --- +**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`). + ## 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 @@ -941,7 +1066,11 @@ the gap as real rather than assume. - **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; +- **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.