Initial public release

Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
This commit is contained in:
Aere Network 2026-07-20 01:01:36 +03:00
commit 13b10718fc
61 changed files with 12616 additions and 0 deletions

View File

@ -0,0 +1,374 @@
# spec-25: The Aere Improvement Proposal (AIP) Process, Index, and Design-Decision Records
| Field | Value |
| --- | --- |
| Spec | 25 |
| Title | The Aere Improvement Proposal (AIP) Process, Index, and Design-Decision Records |
| Author | Aere Network Foundation |
| Status | For publication |
| Created | 2026-07-19 |
| Chain | Aere Network, chain ID 2800 |
## 0. Purpose of this document
This document does three things.
1. It formalizes the **AIP process**: what an Aere Improvement Proposal is, the lifecycle it moves through, who may propose one, where proposals live, and how they are ratified today.
2. It publishes a **canonical index** of every AIP that currently exists in the Aere repositories, with its real number, title, status, and path. It records the real state, including the fact that the process is young and the numbering is short.
3. It drafts the **key design-decision records** for load-bearing architecture choices that are not yet captured under a numbered AIP, continuing the real existing numbering.
The goal is a transparent, numbered, dated record of Aere's reasoning that any auditor, integrator, or automated agent can read and, if they disagree, fork. This is the same intent behind Ethereum's EIP/ERC process and Rust's RFCs. Because it is published, everything in it must be honest.
### 0.1 Honesty note on community review (read this first)
An AIP process is worth running from day one, even before an external technical community exists, because its first job is transparent decision documentation: a stable, plain-text home for the motivation, mechanism, on-chain proof, and honest limitations of each change. That value does not depend on anyone commenting.
The comment and review mechanism defined below is **open**: anyone may author an AIP, raise an issue on one, or fork the record. But it must be stated plainly that Aere has essentially **no external technical community commenting yet**. The network's decentralization is thin (an effective Nakamoto coefficient of about 1, since all validators are Foundation-operated on a single client), and real external usage is thin. Public comment on AIPs becomes meaningful only as an external developer and validator community forms.
Therefore this document, and the AIPs it indexes, contain **no fabricated community discussion**: no invented "the community debated," no fake review threads, no imagined external participants, and no comment counts. Where an AIP records a decision, it records who actually decided (today, the Foundation) and on what evidence. The review mechanism exists now; organic external comment is expected later, and will be recorded honestly when it happens, not simulated in advance.
---
## 1. The process
### 1.1 What an AIP is
An AIP (Aere Improvement Proposal) is a design document describing a change to the Aere Network (chain ID 2800), its contracts, its consensus parameters, or its processes. An AIP records the motivation for a change, the exact mechanism, the rationale for the design, and, when the change is already live, the on-chain addresses and transactions that prove it exists. AIPs are Aere's equivalent of Ethereum's EIP/ERC process. They exist so that a reader, human or machine, can reconstruct why the chain works the way it does from a single canonical plain-text source instead of from marketing pages.
**What belongs in an AIP:** a protocol or consensus change (block time, fee routing, precompiles); a new contract standard or interface other builders integrate against; a token-standard or account-standard convention (ARC); a process or governance change (Meta).
**What does not belong in an AIP:** bug-fix redeploys that preserve an existing interface (tracked in the SDK address registry's deprecation notes), routine parameter tuning inside an already-ratified bound, and pure documentation.
### 1.2 Lifecycle
The normative lifecycle is `Draft -> Review -> Last Call -> Final`. `Living` replaces `Final` for standards that keep evolving (an index, a registry). `Withdrawn` and `Stagnant` are the off-ramps.
| Status | Meaning |
| --- | --- |
| Draft | Well-formed, has an author, may still change substantially. |
| Review | Author has requested wider scrutiny; open questions are tracked in the AIP. |
| Last Call | A stated final-review window (default 14 days). If no blocking issue is raised, it advances. |
| Final | Accepted. For shipped work, live on chain 2800. Breaking changes after Final require a new AIP that supersedes it. |
| Living | Accepted and expected to keep being updated. |
| Withdrawn | Abandoned. Terminal; the number is retired, not reused. |
| Stagnant | Inactive in Draft or Review beyond 6 months. Revivable by any author. |
Retro-filed AIPs (documents written after the change already shipped) enter directly at `Final` or `Living` and state in their Abstract that they backfill history.
### 1.3 Categories and types
Types are **Standards Track**, **Meta**, and **Informational**. Informational AIPs give guidance and do not mandate anything. A Standards Track AIP names exactly one category:
- **Core:** consensus, block production, fee and burn accounting, precompiles, genesis and client configuration.
- **Networking:** peer-to-peer protocol and sync.
- **Interface:** contract interfaces, ABIs, RPC conventions, verification surfaces.
- **ARC** (Aere Request for Comment): application and token standards (ERC-20, ERC-4626, ERC-4337, ERC-6551 conventions as adopted on Aere).
Meta and Informational carry no category.
### 1.4 Roles
- **Author.** Anyone may author an AIP. The author writes the document, drives it through the statuses, and answers review questions.
- **Editor.** Checks that an AIP is well-formed, correctly numbered and categorized, and technically coherent, and that every cited address, transaction, and number is real. The editor does not judge whether a proposal is a good idea; that is the ratifier's job. Until decentralization, the editor is the Foundation.
- **Ratifier.** The party whose approval moves an AIP to Final. Until decentralization this is the Foundation account `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`, a single-key, Foundation-controlled account (not a deployed multisig).
### 1.5 How to file an AIP
1. Copy the template to `AIP-N.md`, where N is the next free integer.
2. Fill in every required section (Preamble, Abstract, Motivation, Specification, Rationale, Backwards Compatibility, Security Considerations, Reference Implementation and On-Chain Deployment, Copyright). A section that does not apply keeps its header with the body "None".
3. Cite on-chain addresses verbatim from the canonical SDK registry (`sdk-js/src/addresses.ts`). Never invent an address, transaction hash, or gas number. If a number has not been measured, write "to be measured".
4. Add a row to the index.
5. Open it for editorial review. Anyone may raise an issue against it. Until decentralization, the editor and ratifier is the Foundation.
### 1.6 Where AIPs live
- **Canonical scaffold:** `aerenew/aips/` holds `README.md`, `aip-template.md`, and the retro-filed records AIP-1 through AIP-7. This is the seed of a public AIP repository.
- **AIP-8** currently lives as a standalone Draft at `aerenew/docs/AIP-PQ-TX-2026-07-18.md` and has not yet been moved into the `aerenew/aips/` index. This document records that real state; consolidating it into the scaffold index is a housekeeping step.
- **Earlier publish-staging copy:** `aere-research-repo/aips/` holds AIP-1 through AIP-6 with an older governance note (it says "three validators" and describes the Foundation account as a "multisig"). It is stale relative to `aerenew/aips/`, which reflects the current reality of seven validators and a single-key Foundation account. The `aerenew/aips/` copy is authoritative where the two differ.
### 1.7 Sourcing rules (binding on every AIP)
- Every on-chain address is copied verbatim from the canonical SDK registry. No address is guessed. If unsure, omit it.
- Every gas figure, transaction hash, and block number is reproducible from the chain or a repo fixture. Unmeasured quantities are written as "to be measured", never invented.
- Design ceilings (for example a theoretical maximum throughput) are never presented as measured throughput.
### 1.8 Honest governance note and the decentralization path
Aere is not yet trustlessly governed. Today the network runs seven QBFT validators (f=2, commit quorum 5-of-7) under a single operator, on one client (Hyperledger Besu), with no external security audit and thin real usage. In that reality an AIP is not ratified by an on-chain vote of independent stakeholders. It is **Foundation-ratified**: the Foundation account is the final editor and approver. Every AIP that predates validator and client decentralization carries the label **"Foundation-ratified (pre-decentralization)"** so no reader mistakes editorial ratification for trustless governance.
The intended migration, each step of which will be its own Meta AIP: (1) open the validator set beyond the current seven Foundation-operated validators and run a second client as a live producer; (2) introduce a public review period the Foundation cannot unilaterally shorten; (3) move ratification to stake-weighted or validator-weighted approval once an independent staker and validator base exists. Until step 3, all Standards Track AIPs carry the pre-decentralization label. The chief risk of a pre-decentralization process, that a single party can both author and ratify, is mitigated only partially today by the editor's sourcing rules and by keeping the honesty label on every proposal; it is fully mitigated only when independent ratifiers exist. This weakness is stated rather than hidden.
---
## 2. Index of existing AIPs (real state)
The numbering is contiguous from 1 to 8, with no gaps. AIP-1 through AIP-7 are ratified and live in `aerenew/aips/`. AIP-8 is a Draft in `aerenew/docs/`. No AIP numbered 9 or higher exists in any Aere repository as of this writing; the records in Section 3 below are the first drafts of AIP-9 onward.
| AIP | Title | Type / Category | Status | Path |
| --- | --- | --- | --- | --- |
| 1 | AIP Purpose and Process | Meta | Living | `aerenew/aips/AIP-1.md` |
| 2 | Coinbase Fee-Burn Routing (37.5%) | Standards Track / Core | Final | `aerenew/aips/AIP-2.md` |
| 3 | Sub-Second Block Period (500 ms QBFT) | Standards Track / Core | Final | `aerenew/aips/AIP-3.md` |
| 4 | On-Chain Post-Quantum Signature Verification Suite | Standards Track / Interface | Final | `aerenew/aips/AIP-4.md` |
| 5 | sAERE Receipt Token and AereSink Immutable Flywheel | Standards Track / ARC | Final | `aerenew/aips/AIP-5.md` |
| 6 | ERC-4337 Passkey and Gasless Onboarding Stack | Standards Track / ARC | Final | `aerenew/aips/AIP-6.md` |
| 7 | AerePQC Hard-Fork Activation (Native PQC Precompiles and Extended EIP-2935 Lookback) | Standards Track / Core | Final | `aerenew/aips/AIP-7.md` |
| 8 | Post-Quantum-Authorized Transaction Envelope (EIP-2718 Type 0x2A) | Standards Track / Core | Draft (not live) | `aerenew/docs/AIP-PQ-TX-2026-07-18.md` |
Notes on real state:
- AIP-1 through AIP-7 are Foundation-ratified (pre-decentralization). AIP-2 through AIP-7 are retro-filed records of changes already live on chain 2800; AIP-1 is the process document itself.
- AIP-8 is a genuine Draft: it specifies a base-layer post-quantum transaction type that requires a coordinated Besu hard fork and is **not deployed**. It ships with a deploy-today ERC-4337 reference path (`AerePQCTxAccount.sol`) that needs no fork.
- The mechanism specification behind AIP-7 also exists as `aerenew/research/aip-draft-pqc-precompiles.md` (frontmatter `aip: TBD`, marked SHIPPED); it is the design/gas spec, canonically recorded as AIP-7. It is not a separate AIP number.
- A duplicate AIP-1 through AIP-6 set exists under `aere-research-repo/aips/`; it is an older copy and is superseded by `aerenew/aips/` where the two disagree (see 1.6).
### 2.1 Coverage map: which section-4 decisions already have a record
The engineering rationale in `aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md` Section 4 enumerates the load-bearing design decisions. Several already have a numbered AIP; the rest are drafted in Section 3.
| Section 4 decision | Captured by | New record needed |
| --- | --- | --- |
| 4.1 Consensus: QBFT, not HotStuff | (none) | Yes -> AIP-9 |
| 4.2 Execution client: forked Besu with Nethermind second, not Reth | (none) | Yes -> AIP-10 |
| 4.3 Interoperability: Hyperlane-compatible plus zk light clients, not IBC | (none) | Yes -> AIP-11 |
| 4.6 Virtual machine: extend the EVM, not a new VM | (none) | Yes -> AIP-12 |
| 4.4 Post-quantum signatures: Falcon and ML-DSA together, not one lattice family | AIP-4, AIP-7 (mechanism) | Yes, as a distinct decision record -> AIP-13 |
| 4.5 Account abstraction: ERC-4337 with EIP-7702 bridge, not native AA | AIP-6 (stack) | Yes, as a distinct decision record -> AIP-14 |
| 4.7 The burn: validator-reward cut, not a base-fee burn | AIP-2 (mechanism and rationale) | No; already recorded in AIP-2 |
| PQC precompile activation | AIP-7 | No; already recorded in AIP-7 |
| In-place hybrid PQC consensus activation (R&D, not live) | (none) | Yes -> AIP-15 |
| PQ STARK-verify precompile 0x0AE8 (skeleton, not live) | (none) | Yes -> AIP-16 |
---
## 3. Design-decision records drafted here (AIP-9 to AIP-16)
These are the first drafts of the records that continue the real numbering after AIP-8. AIP-9 through AIP-14 document architecture decisions that are already live and settled, so they enter at Final as Informational records (they explain rationale for a shipped choice rather than mandate a new normative change). AIP-15 and AIP-16 document research and blocked work that is **not live on chain 2800**, so they enter at Draft. Each record is deliberately concise: context, decision, rationale, alternatives rejected, and status. The normative mechanism for a decision that also has a Standards Track AIP (AIP-13 references AIP-4/AIP-7; AIP-14 references AIP-6) stays in that AIP; the record here captures the decision framing.
The post-quantum scope boundary holds throughout: **Aere consensus is classical secp256k1 ECDSA QBFT.** Post-quantum applies to signatures, accounts, and applications only. No record here claims post-quantum consensus.
---
### AIP-9: Consensus Mechanism: QBFT, not HotStuff
| Field | Value |
| --- | --- |
| AIP | 9 |
| Title | Consensus Mechanism: QBFT, not HotStuff |
| Author | Aere Network Foundation |
| Type | Informational |
| Status | Final |
| Created | 2026-07-19 |
| Ratification | Foundation-ratified (pre-decentralization) |
**Context.** Aere is a settlement layer whose core promise is deterministic, single-slot finality at a sub-second cadence. It runs a small, permissioned validator set: seven validators today, with a roadmap target in the 9 to 21 range.
**Decision.** Use QBFT, the Byzantine-fault-tolerant IBFT-family protocol as implemented in Hyperledger Besu, producing deterministic single-block finality at a target block time of about 0.5 s. At N=7, QBFT tolerates f=2 Byzantine or crashed validators with a commit quorum of 2f+1 = 5-of-7.
**Rationale.** At Aere's validator-set size, QBFT's O(N^2) message complexity is negligible in absolute terms (a 5-of-7 quorum verify costs on the order of a few milliseconds per block, a small fraction of the 500 ms slot). Against that non-cost, QBFT gives three concrete advantages: deterministic instant single-block finality (the exact settlement guarantee the network sells), maturity and existing integration in Besu (no new-protocol implementation risk), and the fact that Aere's post-quantum consensus research, second-client work, and zk finality proofs are all built against QBFT.
**Alternatives rejected.** HotStuff and its pipelined variants, whose linear per-round communication complexity is engineered to scale BFT to hundreds of validators. That scaling regime is one Aere does not operate in and does not plan to enter under this design, so HotStuff's headline benefit is unavailable, while adopting it would add the risk and integration cost of a protocol less battle-tested in the chosen client.
**Honest limitation.** At N=7 the fault tolerance against node outages is real, but all validators are Foundation-operated. Decentralization, not the consensus mechanism, is the binding trust assumption. The block-period choice (500 ms) is documented separately in AIP-3.
**Status.** Final. Live on chain 2800.
---
### AIP-10: Execution Client: Forked Besu with Nethermind as the Second Client, not Reth
| Field | Value |
| --- | --- |
| AIP | 10 |
| Title | Execution Client: Forked Besu with Nethermind as the Second Client, not Reth |
| Author | Aere Network Foundation |
| Type | Informational |
| Status | Final |
| Created | 2026-07-19 |
| Ratification | Foundation-ratified (pre-decentralization) |
**Context.** A production chain needs a producing client and, to retire single-implementation risk, a second independent client. Aere already ships QBFT, which is Besu-specific: no off-the-shelf Geth, Reth, Erigon, or stock Nethermind produces QBFT-compatible blocks.
**Decision.** A fork of Hyperledger Besu v26.4.0 (Java, base commit d2032017, JDK 21 toolchain) is the sole live mainnet producer. The fork adds the native post-quantum precompiles and extended EIP-2935 lookback (live since block 9,189,161, AIP-7) and the 1-Gwei base-fee floor (live since block 10,141,734). A patched Nethermind 1.39.0 (.NET) is the second, independent client.
**Rationale.** Besu already carries QBFT and already carries the codebase into which the PQC precompile fork and base-fee changes were integrated. Nethermind was chosen as the second client specifically for implementation diversity: a different codebase in a different language (.NET versus Java), so a consensus-relevant bug in one is unlikely to exist in the other. The patched Nethermind validates the live chain, runs the five PQC precompiles byte-for-byte identically (27 of 27 NIST KAT vectors), and, on an isolated test network, produces byte-identical QBFT blocks that stock Besu accepts through full BFT validation (with a one-byte-tampered seal rejected).
**Alternatives rejected.** Reth (Rust) as the base client: it has no notion of QBFT, so it would require building QBFT production into a client that lacks it before producing a single block; and the September 2025 Reth halt is the reference case for why depending on one producing implementation is a systemic risk. A second Besu instance instead of Nethermind: that is not client diversity, since it shares every bug with the first.
**Honest limitation.** Cross-client production is proven on an isolated testnet (offline import and, later, live devp2p between test nodes), and a listen-only Nethermind follower has run against the live network. Nethermind is **not yet a live gossiping producer on chain 2800**. Besu remains the sole live producer; the second client is a client-diversity cross-check and fail-safe today, not a second live validator. Making Nethermind a live producer is a founder-supervised change, because a producer bug on a live chain can halt or fork it.
**Status.** Final (as the decision and the current live topology). Live producer: Besu only.
---
### AIP-11: Interoperability: Hyperlane-Compatible Messaging plus zk Light Clients, not IBC
| Field | Value |
| --- | --- |
| AIP | 11 |
| Title | Interoperability: Hyperlane-Compatible Messaging plus zk Light Clients, not IBC |
| Author | Aere Network Foundation |
| Type | Informational |
| Status | Final |
| Created | 2026-07-19 |
| Ratification | Foundation-ratified (pre-decentralization) |
**Context.** Aere's value proposition is that it is an ordinary EVM chain. Cross-chain interop must serve permissionless, EVM-familiar builders while also offering trust-minimized verification of a counterparty's finality.
**Decision.** An EVM-native, Hyperlane-compatible messaging layer (a Mailbox-compatible `AereMessenger`, an interchain gas paymaster `AereIGP`), an ERC-7683 intent layer, and an Across-v3-compatible spoke pool. Alongside it, a zero-knowledge interop path: on-chain zk light clients that verify a counterparty's consensus finality (inbound Ethereum sync-committee finality, outbound Aere QBFT finality) from inside a contract, via SP1 Groth16 proofs.
**Rationale.** IBC's design philosophy, verifying the counterparty's consensus rather than trusting a bridge committee, is the right one, and Aere adopts it directly. Aere splits the problem: for permissionless developer-familiar interop it uses the Hyperlane-compatible interfaces the EVM tooling ecosystem already speaks; for the trust-minimization that is IBC's real contribution it built the zk light clients, which capture "verify the counterparty's consensus, do not trust a relayer" without leaving the EVM world.
**Alternatives rejected.** IBC as a protocol: it is built around Tendermint-style light clients and a non-EVM connection, channel, and packet model. Adopting it natively would import a substantial non-EVM stack and its tooling into a chain whose entire premise is that it is an ordinary EVM.
**Honest limitation.** The Hyperlane-compatible endpoints are interface-faithful and deployed, but a live decentralized relayer and interchain-security-module network is not yet stood up (the current bridge path runs through a single Foundation signer with a relayer pending). The zk light clients are trust-minimized, not trustless, and not quantum-safe: their committed seals are classical secp256k1 and the proof wrap is Groth16 over BN254 (Shor-breakable).
**Status.** Final (as the decision). Deployed interfaces; decentralized relayer pending.
---
### AIP-12: The Virtual Machine: Extend the EVM, not a New VM
| Field | Value |
| --- | --- |
| AIP | 12 |
| Title | The Virtual Machine: Extend the EVM, not a New VM |
| Author | Aere Network Foundation |
| Type | Informational |
| Status | Final |
| Created | 2026-07-19 |
| Ratification | Foundation-ratified (pre-decentralization) |
**Context.** Aere differentiates on post-quantum verification, parallel execution, and extended block-hash lookback. It must add these without taxing the developer surface it depends on.
**Decision.** Keep the EVM. Solidity, EVM bytecode, JSON-RPC, and the developer tooling are unchanged. Differentiation is added as precompiles and client-fork rules at otherwise-empty reserved addresses, not as a new instruction set or a new language, and activated by client-only forks in the same manner Ethereum activates its own hard forks.
**Rationale.** A mature VM has years of implementation hardening, a known cost model, and an operational history; a new VM starts that clock at zero, maximizing technical and adoption risk. The EVM is the single largest smart-contract developer surface in existence, and every deviation (a nonstandard opcode, account model, or RPC) is a tax on every developer and tool. Aere's thesis is settlement, not a novel programming model, so the design keeps the tested execution environment intact and adds differentiation only at the EVM's standard extension points (precompiles at reserved addresses). Nothing about Aere's account, passkey, or post-quantum surface depends on a nonstandard EVM.
**Alternatives rejected.** Designing a new, purpose-built VM (a new bytecode, language, or non-EVM environment) optimized around Aere's differentiators.
**Honest limitation.** Some ambitions (base-layer parallel execution, an EVM-in-a-zkVM) must be reached inside the existing model rather than designed in from scratch. Those remain roadmap and are labeled as such.
**Status.** Final. Live on chain 2800.
---
### AIP-13: Post-Quantum Signatures: Falcon and ML-DSA Together, not a Single Lattice Family
| Field | Value |
| --- | --- |
| AIP | 13 |
| Title | Post-Quantum Signatures: Falcon and ML-DSA Together, not a Single Lattice Family |
| Author | Aere Network Foundation |
| Type | Informational |
| Status | Final |
| Created | 2026-07-19 |
| Requires | 4, 7 |
| Ratification | Foundation-ratified (pre-decentralization) |
**Context.** Aere verifies post-quantum signatures on-chain at both the contract layer (AIP-4) and, since block 9,189,161, the native precompile layer (AIP-7). A choice of scheme family is a bet on a mathematical assumption.
**Decision.** Support multiple post-quantum signature schemes across two distinct lattice families, Falcon (Falcon-512 and Falcon-1024) and ML-DSA (Dilithium2 / ML-DSA-44), alongside hash-based schemes (SLH-DSA, XMSS, WOTS+). The live precompiles for SHAKE256, Falcon-512, Falcon-1024, ML-DSA-44, and SLH-DSA-SHA2-128s occupy the 0x0AE1 to 0x0AE5 band.
**Rationale.** Cryptographic-risk diversification. Falcon rests on NTRU lattices; ML-DSA rests on module lattices with Fiat-Shamir-with-aborts. Supporting both means a cryptanalytic break or a standardization flaw in one lattice family does not, by itself, remove Aere's ability to verify post-quantum signatures: accounts and settlement authorization can migrate to the surviving family. This mirrors the hybrid `AereHybridAuth` account, which requires both a classical ECDSA and a Falcon-512 signature so that neither a broken curve nor a broken lattice alone suffices to forge.
**Alternatives rejected.** Standardizing on a single lattice scheme (ML-DSA alone as the NIST primary, or Falcon alone for its compact signatures). The cost of supporting several schemes is more verifier surface to implement, validate, and eventually audit; that cost is accepted deliberately rather than betting the network's post-quantum future on a single assumption.
**Scope boundary (binding).** This is post-quantum at the signature, account, and application layer only. Consensus remains classical (validators sign secp256k1 QBFT), and the zk verification is classical (BN254 Groth16, Shor-breakable). Aere never claims post-quantum consensus.
**Honest limitation.** The verifiers and precompiles carry an internal self-audit only; an external audit is pending before they should secure material value. Their assurance today rests on bit-for-bit agreement with official NIST KAT and ACVP vectors and cross-checks against independent reimplementations.
**Status.** Final. Precompiles live on chain 2800; the normative mechanism is in AIP-4 (contract suite) and AIP-7 (precompile activation).
---
### AIP-14: Account Abstraction: ERC-4337 with EIP-7702 Bridge, not Native AA
| Field | Value |
| --- | --- |
| AIP | 14 |
| Title | Account Abstraction: ERC-4337 with EIP-7702 Bridge, not Native AA |
| Author | Aere Network Foundation |
| Type | Informational |
| Status | Final |
| Created | 2026-07-19 |
| Requires | 6 |
| Ratification | Foundation-ratified (pre-decentralization) |
**Context.** Aere wants programmable accounts (recovery, passkey ownership, post-quantum ownership, gas sponsorship) without breaking EVM and EOA compatibility.
**Decision.** ERC-4337 (v0.7) smart accounts as the account-abstraction path, with EIP-7702 (live under the Pectra ruleset) as the migration bridge that lets existing externally-owned accounts (EOAs) delegate to smart-account code.
**Rationale.** ERC-4337 needs no consensus change, is an established standard with an existing bundler and tooling ecosystem, and delivers programmable accounts (including the Falcon-owned post-quantum smart account `AerePQCAccountFactory`) without touching the protocol, preserving exact EVM and Ethereum compatibility. EIP-7702 is the chosen bridge precisely because it upgrades existing EOAs in place without forcing a disruptive "everything is a contract" change.
**Alternatives rejected.** Native, protocol-level account abstraction up to an "all accounts are smart accounts by default" end-state: it changes what an account fundamentally is, breaking existing tooling, contracts, and user keys. EIP-3074, an earlier EOA-empowerment proposal: it was superseded by EIP-7702, which is the mechanism Aere carries; Aere does not implement EIP-3074.
**Honest limitation.** The EntryPoints are Aere's own ERC-4337-compatible implementations, compatible in shape and interface but not byte-identical to Ethereum's canonical audited singleton; integrators should treat them as Aere-specific. Gasless onboarding is subsidized (Foundation-funded, rate-limited), not free. These contracts have not had an external audit.
**Status.** Final. Live on chain 2800; the normative stack (accounts, factories, paymasters, EntryPoints) is in AIP-6.
---
### AIP-15: In-Place Hybrid PQC Consensus Activation via On-Chain Anchor Contract, not Re-Genesis
| Field | Value |
| --- | --- |
| AIP | 15 |
| Title | In-Place Hybrid PQC Consensus Activation via On-Chain Anchor Contract, not Re-Genesis |
| Author | Aere Network Foundation |
| Type | Standards Track |
| Category | Core |
| Status | Draft (isolated-testnet R&D; not live on chain 2800) |
| Created | 2026-07-19 |
| Ratification | Not ratified; external-audit and founder gated |
**Context.** Aere's hybrid consensus research (Stage 1 and Stage 2) proved a gossiped, blocking Falcon-512 quorum certificate that runs alongside the ECDSA committed-seal quorum, empirically at N=4 (2 s) and N=7 (0.5 s). Its Falcon registry, however, was bound to chain identity at genesis (folding `keccak256(manifest)` into the genesis state root). Chain 2800 launched long before any Falcon manifest existed, so blocking activation was previously recorded as "requires a re-genesis," a non-starter for a live chain because re-genesis destroys history and chain identity.
**Decision.** Bind the Falcon manifest to on-chain state **post-genesis**, via a minimal, immutable, ownerless anchor contract deployed by one ordinary transaction at an activation height. The contract commits `keccak256(manifest)` to storage slot 0 (its runtime code is a single STOP byte; it is never callable and has no admin). On every block import the validation rule resolves the manifest from the **parent block's world state** at the anchor address and activates the pending registry only if the observed hash matches, failing closed on any mismatch. The re-genesis requirement is removed.
**Rationale.** The late anchor folds `keccak256(manifest)` into every block hash from the deployment block onward, so two nodes that agree on the chain past that block necessarily agree on the anchored hash: the same integrity guarantee the genesis ceremony gave, minus the re-genesis. The deploy address is deterministic (`keccak256(rlp[deployer, nonce])[12:]`), so validators can be pre-configured before deployment, and activation is idempotent and retried each block, so deploy-then-configure and configure-then-deploy both converge.
**Alternatives rejected.** Re-genesis carrying a Falcon anchor in genesis alloc (destroys history and chain identity). A shared per-node manifest file with no on-chain root (a node with a tampered manifest could join silently; the on-chain anchor makes it fail closed instead).
**Evidence and honest scope.** Proven end-to-end on an **isolated N=4 @ 0.5 s testnet** (chainId 440855) whose genesis is byte-shape-identical to a classical ECDSA-only chain: classical start, deploy anchor at block 16, activate from the contract at block 17, flip to blocking at fork block 150, with 0 forks, 0.5 s cadence maintained, pre-activation blocks still valid, a post-activation sub-quorum block rejected (chain halts), and recovery in place with no re-genesis. **Nothing here is live on chain 2800.** Chain 2800 consensus remains classical secp256k1 ECDSA QBFT at N=7, with no Falcon layer of any kind in its headers. The hybrid can only shrink the committable set, so it is never worse than the ECDSA-only chain; ECDSA committed seals remain the decisive safety and liveness seal.
**Remaining gates to a live flip.** No longer a gate: re-genesis. Still required: an external audit of the Falcon consensus patch (quorum, gossip, assembly, late-anchor Java) and of BouncyCastle Falcon-512 on the consensus path; finalizing the full target validator set (N >= 9 recommended, since N=7 blocking has a zero two-fault margin) with an address-bound manifest re-anchored for the full set; a live isolated N=7 (ideally N=9) soak of the combined activation; and an explicit founder GO. No agent activates blocking consensus on chain 2800.
**Status.** Draft. Reference patch: `aerenew/consensus-pqc/besu-consensus-pqc-fork-activation.patch` with evidence under `aerenew/consensus-pqc/inplace-activation-evidence/`.
---
### AIP-16: PQ STARK-Verify Precompile (0x0AE8)
| Field | Value |
| --- | --- |
| AIP | 16 |
| Title | PQ STARK-Verify Precompile (0x0AE8) |
| Author | Aere Network Foundation |
| Type | Standards Track |
| Category | Core |
| Status | Draft (reference skeleton; verifies nothing; not live on any Aere network) |
| Created | 2026-07-19 |
| Ratification | Not ratified; prerequisite-gated, external-audit gated, founder gated |
**Context.** Aere's zk interop and validity paths currently wrap proofs as Groth16 over BN254, which is classical and Shor-breakable. A post-quantum-sound proof system (a FRI-based STARK, hash-commitment only, no pairing) would remove that assumption. A native precompile is the natural home for STARK verification, matching the same reasoning used for the PQC signature precompiles in AIP-7.
**Decision.** Reserve precompile address 0x0AE8 for a PQ STARK-verify precompile that verifies a hash-based STARK directly on-chain (removing a BN254 pairing check). It is specified to activate under a new, forward-dated milestone (PQ-3), never folded into an already-crossed milestone, so that pre-activation history is byte-identical on old and new binaries and the upgrade itself carries no fork risk.
**Scope caveat (2026-07-19 research finding).** An earlier draft described this as verifying "an SP1 inner-STARK (FRI over a Poseidon2 commitment)". That is corrected: the verifier skeleton built so far is a BabyBear + FRI STARK verifier, conformance-confirmed against Plonky3 `0.4.3-succinct`, which targets Aere's OWN Plonky3 circuits, NOT SP1 6.1.0. The pinned SP1 6.1.0 is a Hypercube release (KoalaBear multilinear: BaseFold plus sumcheck-zerocheck plus LogUp-GKR), so FRI does not apply to it and this skeleton does not verify SP1 6.1.0 proofs; retargeting 0x0AE8 to the SP1 Hypercube stack is a separate ~22 to 32 person-week effort. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
**Rationale.** A forward-dated, non-retroactive milestone plus a rolling binary upgrade (the same zero-liveness-risk posture proven for the base-fee floor and the in-place activation) means the address stays an empty account, and every consumer adapter fails closed, until the announced activation time. A migrated consumer can keep both the new 0x0AE8 path and the existing BN254 gateway required during a defense-in-depth window before dropping the classical path.
**Alternatives rejected.** Folding 0x0AE8 into the crossed `futureEipsTime` milestone (block 9,189,161): every historical block that called the currently-empty address would re-execute with different return data on the upgraded binary, a state divergence and a consensus split. Presenting the current artifact as a working verifier: it is not one.
**Honest scope (critical).** This is the record's load-bearing honesty statement. **0x0AE8 is not activated on any Aere network. As of this date it is a reference skeleton that fail-closes (returns EMPTY for every input): it is not a working verifier and must not be presented as one.** Mainnet 2800 has exactly five PQC precompiles (0x0AE1 to 0x0AE5, block 9,189,161). The sibling ML-KEM-768 (0x0AE6) and Falcon HashToPoint (0x0AE7) precompiles are testnet-only. `AerePQStarkVerifier.sol` is a fail-closed adapter that denies every proof (safe by construction). Consensus is untouched by all of this; it remains classical ECDSA QBFT.
**Prerequisite and gates.** Activation cannot begin until the crypto core is ported from maintained references (Plonky3 FRI/Poseidon2/uni-stark, SP1 stark) or a pinned native wrap is built and reproducible; a real SP1 v6.1.0 inner-STARK vector corpus exists and the precompile accepts every valid vector and rejects every tampered or wrong-public-values vector, including cross-agreement with the live BN254 gateway; a soundcalc receipt for the exact pinned FriConfig is recorded (no 128-bit claim without the receipt); and a benchmark confirms a real verify fits under the EIP-7825 2^24 per-transaction gas cap. Beyond the prerequisite: a specialist external STARK/FRI audit of the consensus-critical crypto, a conformance gate green on the frozen artifact, an isolated N=7 soak across a simulated milestone crossing, and an explicit founder GO. Until all hold, 0x0AE8 stays a skeleton.
**Status.** Draft (blocked on the prerequisite crypto-core port). References: `aerenew/docs/PQ-STARK-VERIFIER-PRECOMPILE-2026-07-18.md` (design), `aerenew/docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md` (gated activation plan), `aerenew/contracts/contracts/zkverify/AerePQStarkVerifier.sol` (fail-closed adapter).
---
## 4. Copyright
Released to the public domain (CC0). No rights reserved.

104
AERE-ARCHITECTURE-MAP.md Normal file
View File

@ -0,0 +1,104 @@
# Aere Network Architecture Map
Aere Network is a public, EVM-compatible Layer 1 (chain ID 2800). One diagram replaces pages of prose here: the architecture is best read as a layered stack (Consensus at the bottom, then Execution, Accounts, and Applications on top) with a set of cross-cutting concerns (post-quantum cryptography, zero-knowledge verification, and crypto-agility) that traverse every layer rather than sitting in any single one. This is deliberately not one linear top-to-bottom column: the layers stack, and the transversal concerns cut across all of them.
The single most important honesty point, stated up front: consensus today is classical ECDSA (secp256k1) QBFT. Only signatures, accounts, and precompiles are post-quantum. There is no post-quantum consensus live on mainnet, and the on-chain zero-knowledge verifiers are classical BN254 (Shor-breakable) today. Everything below is marked LIVE (on mainnet 2800) or "(roadmap)" (built and tested, but not activated on mainnet).
## Diagram
```mermaid
flowchart TB
classDef live fill:#e7f6ec,stroke:#1a7f37,color:#0b2b16;
classDef road fill:#fff3e0,stroke:#b26a00,color:#3d2600,stroke-dasharray:5 3;
classDef cross fill:#eaeefb,stroke:#3b46c4,color:#1a1f5c;
legend["Legend. Green solid, LIVE on mainnet 2800. Orange dashed, roadmap (built and tested, not yet live). Blue, cross-cutting concern. Suffix (roadmap) also marks any not-yet-live item inside a label."]:::cross
subgraph APP["Applications layer"]
direction TB
app_defi["DeFi. lending, sAERE staking, AereSink burn"]:::live
app_settle["Settlement. SettlementHub, state channels, AERE402 machine payments"]:::live
app_bridge_l["Bridge and interop. Hyperlane-compatible bus, zk light clients inbound and outbound"]:::live
app_bridge_r["ERC-7683 intent settlement route (roadmap)"]:::road
app_id_l["Identity and compliance. ZKScreen, compliant privacy pool, AereProof registry"]:::live
app_id_r["zk-KYC and over-18 attribute proofs (roadmap)"]:::road
app_ai_l["AI agents. AgentDID, AIReputation, agent bond, InferNet audit log"]:::live
app_ai_r["AI runtime and zk proof of inference (roadmap)"]:::road
end
subgraph ACC["Accounts layer"]
direction TB
acc_eoa["EOA"]:::live
acc_pk["WebAuthn P-256 passkey accounts"]:::live
acc_4337["ERC-4337 v0.7 smart accounts"]:::live
acc_7702["EIP-7702 EOA delegation"]:::live
acc_falcon["Falcon-512 and hybrid ECDSA plus Falcon accounts"]:::live
acc_thr["t-of-n PQC threshold accounts"]:::live
acc_note["Note. flawed originals superseded, some V2 redeploys founder-gated"]:::cross
end
subgraph EXE["Execution layer"]
direction TB
exe_evm["EVM. Pectra plus Fusaka parity"]:::live
exe_pqc["PQC precompiles 0x0AE1 to 0x0AE5"]:::live
exe_pqc2["PQC precompiles ML-KEM-768 and HashToPoint, 0x0AE6 to 0x0AE7 (roadmap)"]:::road
exe_stm["Parallel execution, Block-STM (roadmap)"]:::road
exe_zk["zk validity rollup, revm in SP1 (roadmap)"]:::road
end
subgraph CON["Consensus layer"]
direction TB
con_qbft["QBFT, classical ECDSA secp256k1"]:::live
con_besu["Hyperledger Besu producer, N equals 7"]:::live
con_fin["about 0.5s deterministic finality"]:::live
con_nm["Nethermind 2nd client, validate and follow"]:::live
con_nmp["Nethermind live block producer (roadmap)"]:::road
end
subgraph XC["Cross-cutting concerns, span all layers"]
direction TB
x_pqc["PQC. precompiles, accounts, signatures LIVE. Consensus PQC (roadmap)"]:::cross
x_zk["ZK verify. on-chain BN254 Groth16 via SP1, Halo2, RISC Zero, all classical LIVE. PQ STARK verifier (roadmap)"]:::cross
x_agile["Crypto-agility. schemes addressable and swappable by the calling contract"]:::cross
end
APP -->|builds on| ACC
ACC -->|builds on| EXE
EXE -->|builds on| CON
XC -.spans.-> APP
XC -.spans.-> ACC
XC -.spans.-> EXE
XC -.spans.-> CON
```
## Layer-by-layer map
### Consensus (LIVE)
Aere Network runs QBFT (Istanbul BFT family) with a Hyperledger Besu block producer, signing blocks with classical ECDSA over secp256k1. The live validator set is N=7 (f=2, commit quorum 5 of 7), all Foundation-operated, at a 500 ms target block period with sub-second deterministic finality. A second execution client, a patched Nethermind 1.39.0, is LIVE as a client-diversity cross-check: it peers with the Besu validators over devp2p and independently validates and follows the live chain (verification-layer diversity). Nethermind producing live QBFT blocks is roadmap: block production and adversarial recovery are proven on isolated testnets and via offline import (Besu imports Nethermind-sealed blocks byte-for-byte), but it is not yet a live producing validator on mainnet. Consensus is classical today, not post-quantum.
### Execution (LIVE base, frontier roadmap)
The EVM is at Pectra plus Fusaka parity with Ethereum mainnet (Prague/Cancun activated at block 2,075,363; Osaka at block 2,106,606), including EIP-7702, EIP-2537 BLS12-381, EIP-2935 historical block hashes, transient storage, and the RIP-7951 P-256 precompile at 0x100. Five native post-quantum precompiles are LIVE on mainnet since block 9,189,161 (Falcon-512, Falcon-1024, ML-DSA-44, SLH-DSA-128s, SHAKE256 at 0x0AE1 through 0x0AE5), NIST KAT-validated on-chain. Roadmap execution items, all built and tested but not activated on mainnet: two further precompiles (ML-KEM-768 at 0x0AE6 and Falcon HashToPoint at 0x0AE7, KAT-verified on isolated testnet); parallel execution via a Block-STM engine (correctness proven bit-identical to sequential, near-linear execution-phase scaling, but no end-to-end throughput win claimed and not enabled on L1); and a full-EVM zk validity rollup (revm in the SP1 zkVM), which is verified on-chain as a proof-of-approach on small real blocks, not yet a production-throughput rollup.
### Accounts (LIVE, some V2 redeploys founder-gated)
Aere supports the full modern account spectrum: plain EOAs; WebAuthn / P-256 passkey accounts (backed by the native 0x100 precompile); ERC-4337 v0.7 smart accounts (AereEntryPointV2 live); EIP-7702 EOA delegation; a Falcon-512 post-quantum account whose sole owner is a Falcon public key; hybrid ECDSA plus Falcon authorization; and non-custodial t-of-n post-quantum threshold accounts (AereThresholdAccountFactory V2 live on mainnet, each committee leg verified by the live PQC precompiles). Honest caveat: several first-generation account contracts had audited defects and were superseded by fixed V2 redeploys that hold zero value; some corrected factories are deployed inert and the live migration of any funds-bearing path is founder-gated.
### Applications (mixed LIVE and roadmap)
- DeFi (LIVE): an isolated-market lending engine (AereLendingMarket, proven end-to-end with real supply/borrow/repay), the sAERE staking-receipt vault, and the immutable AereSink burn-and-buyback router. Note that sAERE has a corrected V2 whose migration is founder-gated.
- Settlement (LIVE): AereSettlementHub, AereStateChannels (24h challenge window), and AERE402 machine-to-machine payments (AereAgentV2 plus AERE402FacilitatorV2). The settlement hub is deployed in a bootstrap state holding no value pending a founder-supervised migration.
- Bridge and interop: Hyperlane-compatible message bus (AereMessenger) is LIVE, as are the inbound zk Ethereum light client and the outbound zk QBFT-finality light client (both proven live end-to-end, trust-minimized, classical BN254). ERC-7683 intent settlement (SpokePool path) is roadmap: the corrected contracts exist but are deployed inert, so no live intent route is enrolled.
- Identity and compliance: ZKScreen v3 sanctions screening is LIVE, as is a compliant, sanctions-gated privacy pool (deposit/withdraw circuit proofs are still being generated) and the AereProof proof-anchoring registry. zk-KYC and the over-18 attribute proof are roadmap (proofs generated, pending a Foundation registration signature).
- AI agents: AgentDID (Falcon-rooted decentralized identity), AIReputation, the slashable agent bond, and the InferNet inference-audit-log commitments (EU AI Act style) are LIVE. A general AI runtime and a zero-knowledge proof-of-inference (zkML) verifier are roadmap.
### Cross-cutting: PQC (transversal)
Post-quantum cryptography is not a layer, it runs through the stack. LIVE: the execution-layer PQC precompiles, the PQC accounts, and application-layer PQC signature verification (for example AerePQCAttestation, proven usable end-to-end on mainnet). Roadmap: post-quantum consensus. A hybrid ECDSA plus Falcon dual-quorum design, an on-chain Falcon registry, and in-place activation without re-genesis are all proven on isolated testnets and formally analyzed as no-worse-than-ECDSA, but they are not activated on mainnet and remain audit-plus-founder gated. Consensus stays classical ECDSA QBFT today.
### Cross-cutting: ZK verification (transversal)
Aere exposes a multi-prover verification surface. The on-chain verifiers that are LIVE today are all classical BN254: the SP1 Groth16 gateway, a Halo2 PLONK-over-BN254 (KZG) verifier, and a RISC Zero Groth16 verifier. These underpin ZKScreen, the privacy pool, the zk light clients, and the zk validity rollup anchors. Because BN254 pairings are Shor-breakable, this surface is not post-quantum. A post-quantum STARK verifier is roadmap. Scope caveat (2026-07-19 finding): the 0x0AE8 verifier skeleton built so far is a BabyBear + FRI STARK verifier, conformance-confirmed against Plonky3 and targeting Aere's OWN circuits; it does NOT yet verify SP1 6.1.0 proofs, because the pinned SP1 6.1.0 is a Hypercube (KoalaBear multilinear) system, a different stack. Replacing the SP1 BN254 Groth16 path is a separate ~22 to 32 person-week retarget (`AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`), not done or imminent.
### Cross-cutting: crypto-agility (transversal)
Signature and proof schemes are addressable and swappable by the calling contract rather than hard-wired. AereCryptoRegistry (live) maps scheme identifiers to concrete precompiles or verifiers, with additional reserved scheme slots and a governance rotation runbook, so applications can migrate primitives (for example add or rotate a PQC scheme) without redesigning the stack.
---
Convention recap: plain / green means LIVE on mainnet chain 2800; "(roadmap)" / orange dashed means built and tested but not yet activated on mainnet. Consensus is classical ECDSA QBFT; PQC is signatures, accounts, and precompiles only; on-chain ZK verifiers are classical BN254.

403
AERE-BENCHMARK-REPORT.md Normal file
View File

@ -0,0 +1,403 @@
# Aere Network: Reproducible Benchmark Report (spec-18)
**Status date:** 2026-07-19
**Chain:** Aere Network mainnet, chain ID 2800 (Hyperledger Besu QBFT).
**Scope:** chain performance, zkVM proof-verification economics, and post-quantum
cryptographic-operation gas, stated with a hard separation between what is
freshly measured, what is cited from a committed repo artifact, and what still
needs a measurement command run.
## The absolute rule this report obeys
No performance number (TPS, latency, gas, proof size, proving time) appears here
unless it was either (a) freshly measured by running a command this session, or
(b) found in a committed repo artifact that is cited by path. Anything that
cannot be measured or sourced is written as a measurement command and marked
`[MEASURE]`. Anything sourceable only from memory and not yet confirmed against
code is marked `[VERIFY]`. A fabricated benchmark would be a catastrophic
failure of this document, so the labels below are load-bearing, not decoration.
### Label legend
| Label | Meaning |
|---|---|
| `[MEASURED-FRESH]` | Run by this report on this machine, output shown or summarized. |
| `[CITED: path]` | Value read from a committed repo artifact at the cited path. |
| `[MEASURE: cmd]` | Not measured here. The command that would measure it is given. |
| `[VERIFY: note]` | Sourceable but needs confirmation against live code or a live node. |
### Scope boundary that never moves
Aere Network validators sign classical secp256k1 QBFT consensus messages.
Nothing in this report makes Aere consensus post-quantum. The post-quantum work
is an application and account layer capability. Any claim of post-quantum
consensus would be false and is not made here.
---
## PART A: CHAIN PERFORMANCE
### A.0 The one honesty point, stated first
The "273,000 TPS" figure is an ARCHITECTURAL CEILING derived from block-space
arithmetic under ideal conditions. It is NOT the live mainnet transaction rate,
it has never been a measured result, and it is not asserted as one. Three
distinct quantities must never be conflated:
| Quantity | Value | Label |
|---|---|---|
| Architectural ceiling (design maximum) | ~273,000 TPS | `[CITED]`, explicitly labeled a ceiling |
| Testnet-measured L1 throughput | none exists | see A.1 |
| Live-mainnet-observed throughput | far below the ceiling, demand-limited | see A.1 |
`[CITED: aerenew/docs/WHITEPAPER-V2.md lines 73, 117, 633; aerenew/docs/wp2-sections/01-overview.md:59; 02-architecture.md:21; 09-governance-roadmap.md:294]`
The whitepaper states it verbatim: the block parameters "admit a design ceiling
on the order of 273,000 transactions per second; this is a theoretical maximum
implied by the gas and block configuration, not a measured or sustained figure,
and realized throughput on the live network is a small fraction of it." It is
described as "a design ceiling derived from block-space arithmetic under ideal
conditions" that "has never been a measured result."
**On the derivation itself.** The repo labels 273,000 as a block-space-arithmetic
ceiling but does not publish the explicit factors. The two facts that are
sourceable:
- Block period 0.5 s. `[CITED: aerenew/docs/wp2-sections/02-architecture.md:19; genesis-subsecond.json (blockperiodmilliseconds: 500)]`
- Genesis block gas limit is ambiguous across genesis artifacts: `0x2625a00`
(40,000,000) in `aerenew/genesis.json`, versus `0x1fffffffffffff`
(9,007,199,254,740,991, effectively unbounded) in `aere-genesis-current.json`
and `genesis-subsecond.json`. `[VERIFY: which gas limit is live on 2800]`
Implied arithmetic (transparent, not a new claim): 273,000 TPS at a 0.5 s block
is 136,500 tx per block, and at the 21,000-gas simple-transfer floor that is
about 2.87 billion gas per block. That is far above the 40M limit and far below
the effectively-unbounded limit, so the ceiling presumes an effectively-
unbounded block gas limit AND the cheapest possible transaction, not the
EIP-7825-capped realistic workload the chain actually runs. `[VERIFY: publish the
explicit block-space arithmetic behind 273,000 so the ceiling is reproducible]`
### A.1 Testnet-measured and live-observed throughput (the honest gap)
**There is no testnet measurement that validates 273,000 TPS for L1.** The
closest measured throughput number in the repo is a rollup-executor
microbenchmark, and the artifact that produces it says plainly it neither
reaches nor implies the ceiling.
`[CITED: aerenew/parallel-executor/BLOCKSTM-ENDTOEND-THROUGHPUT-2026-07-13.md section 7]`
The Block-STM end-to-end harness measured about 247,000 to 270,000 TPS at
`gas=80` on a 16-vCPU box, but this is (a) the rollup executor with a light
commit proxy, not L1, not a real Merkle Patricia Trie, and (b) entirely a
function of the assumed per-transaction cost: the same box reports 115,000 to
500,000 TPS as the per-transaction weight changes. The `gas=80` proximity to
273,000 is called out in the artifact as a coincidence, not a validation.
**Live mainnet 2800 runs far below the ceiling, and this is by demand, not by
capacity.** `[CITED: aerenew/research/specs/spec-zk-stack.md section 0.2; aerenew/docs/wp2-sections/01-overview.md:59]`
On-chain usage is thin (single-digit proof records for most verifiers; six
genesis user wallets per project memory). Real throughput is demand-limited, not
capacity-limited: the chain has ample block-space headroom and almost no load to
fill it. `[MEASURE: sample a live window with eth_getBlockByNumber over N recent
blocks against https://rpc.aere.network and divide total tx by elapsed time to
publish an observed sustained TPS; expected result is near zero on an idle chain]`
### A.2 Block time and finality
Deterministic single-slot QBFT finality at a 0.5 s block period. Because QBFT
finalizes on commit rather than by accumulation of work, a transaction is
irreversible in well under a second from inclusion, with no confirmation count
and no reorg. `[CITED: aerenew/docs/wp2-sections/02-architecture.md:19]`
The 0.5 s period was reached by a mid-chain QBFT parameter change at block
2,138,451, halved from the launch value of one second, with no re-genesis.
`[CITED: aerenew/docs/wp2-sections/01-overview.md:59; aerenew/docs/WHITEPAPER-V2.md:73; genesis-subsecond.json (blockperiodseconds: 0, blockperiodmilliseconds: 500)]`
Finality is a latency-to-irreversibility claim and is separate from throughput.
The 0.5 s finality claim follows mechanically from QBFT plus the block period; it
says nothing about TPS.
### A.3 Parallel execution (Block-STM): the honest result
The honest result has three parts, and only the first is a mainnet-relevant
guarantee. No end-to-end L1 throughput win is claimed, and Block-STM is NOT
enabled on L1.
**(1) Correctness proven bit-identical to sequential.** `[MEASURED-FRESH]` This
session ran the executor's `bench` on this Windows dev machine. The
`parallel==sequential` gate (committed state and keccak state root identical to
the sequential oracle) passed on all four conflict profiles:
```
LOW-CONFLICT parallel==sequential check: PASS
MEDIUM-CONFLICT parallel==sequential check: PASS
HIGH-CONFLICT parallel==sequential check: PASS
PATHOLOGICAL parallel==sequential check: PASS
```
This corroborates the cited larger runs: a 6,000-comparison correctness harness
with 0 mismatches, and a re-import plus validation of 253 real exported blocks
with zero state-root mismatch. `[CITED: aerenew/parallel-executor/BLOCKSTM-ENDTOEND-THROUGHPUT-2026-07-13.md section 3 and section 8]`
**(2) Near-linear execution-phase scaling on parallelizable workloads.**
`[MEASURED-FRESH]` on this machine (fewer physical cores than the reference
16-core Linux box, so absolute speedup is lower, which is expected and honest):
| Workload (this box, 16 threads) | speedup vs sequential | abort% |
|---|--:|--:|
| Low-conflict (~2%) | 4.36x | 0.3% |
| Medium-conflict (~15%) | 4.96x | 2.3% |
| High-conflict (single hot counter) | 1.92x | 24.8% |
| Pathological (all same slot) | 0.43x (correctly slower) | 11.5% |
`[CITED: same artifact, section 4-5]` The reference 16-core Linux box measured
about 8.3x to 9.4x execution-phase and about 8.3x to 9.1x end-to-end on realistic
0 to 50 percent conflict. The pathological all-same-slot profile is honestly
slower than sequential (correct Block-STM behavior), on both boxes.
**(3) No end-to-end throughput win claimed, and not on L1.**
`[CITED: aerenew/research/specs/spec-parallel-execution.md section 9; BLOCKSTM-ENDTOEND section 7-8]`
The Aere L1 mainnet executes sequentially on Besu. The parallel executor is a
bounded four-kind VM (Transfer, Sweep, Increment, AmmSwap) over a balance and
storage map, not a full EVM, and its measured speedups are single-machine
microbenchmarks, not a network throughput or TPS claim. The real forked client's
commit path was separately measured to anti-scale (parallel commit was slower
than sequential), so the real-client end-to-end ceiling is tighter than the Rust
harness, not looser. The invariant deliverable is the correctness gate plus the
speedup curve and its Amdahl bound, not an absolute TPS.
---
## PART B: ZKVM COMPARATIVE (SP1 vs RISC Zero vs Halo2)
Every cell is labeled. Gas and proof-size values that are cited come from repo
deployment artifacts and are measured single-run values, not benchmarked
averages (the source spec states this explicitly). `[CITED: aerenew/research/specs/spec-zk-stack.md footer]`
### B.1 Comparison table
| Prover (version) | Proving time (off-chain) | On-chain gas to verify | Proof size | Verification latency |
|---|---|---|---|---|
| SP1 Groth16 (v6.1.0) | app-dependent, CITED single-runs: zkscreen ~72 to 75 s, over18 ~71 to 73 s, zkml-mnist ~200 to 205 s, aggregation Groth16 526.1 s `[CITED: contracts/deployments/proof-aggregator-scale.json]` | ~300k typical `[CITED: spec-zk-stack section 2]`; measured records: storage-proof 348,345, zkML 326,492, aggregation ~390k flat `[CITED: proof-aggregator-scale.json, zkml/storage-proof artifacts]` | ~260 to 356 bytes `[CITED: spec-zk-stack section 2]` | one staticcall, final at next block (~0.5 s) `[MEASURE: time eth_call wall-clock]` |
| SP1 Plonk (v6.1.0) | `[MEASURE: prove a fixture with the Plonk prover]` | "somewhat higher than Groth16" `[CITED: spec-zk-stack section 2]`, exact `[MEASURE]` | `[MEASURE]` | one staticcall `[MEASURE]` |
| RISC Zero Groth16 (5.0.0-rc.1) | `[MEASURE: r0vm prove the factor-guest]`, not recorded in repo | `[MEASURE: eth_call gas on RiscZeroVerifierRouter.verify]`, no gas number committed | seal 260 bytes `[CITED: contracts/deployments/risc0-verifier-fix.json sealLen]` | one staticcall `[MEASURE]` |
| Halo2 (bn254 / KZG, SHPLONK) | `[MEASURE: halo2 prover on the cubic circuit]` | ~449,517 gas `[CITED: contracts/deployments/halo2-cubic.json; spec-zk-stack section 6]` (revm gas ~300,144) | 1,152 bytes `[CITED: same]` | one call `[MEASURE]` |
| KZG / EIP-4844 point-eval precompile (0x0A) | n/a (precompile, no prover) | ~255,719 gas `[CITED: contracts/deployments/kzg-verifier.json; spec-zk-stack section 5]` | 192-byte precompile input `[CITED: same]` | one staticcall |
Notes on the two recursion facts worth keeping, both `[CITED: proof-aggregator-scale.json; spec-zk-stack section 7]`:
- A 10-proof recursive SP1 aggregation records on-chain in ~393,844 gas, roughly
flat versus the 3-proof fold (~387,858 gas). Constant on-chain verification cost
regardless of fold count is the point of recursion.
- The proving side of that same 10-fold cost 526.1 s for the aggregation Groth16
plus 988 s of inner proving (measured, off-chain, single run).
### B.2 Routing logic (which prover for which job, and why)
`[CITED: aerenew/research/specs/spec-zk-stack.md sections 1-3, 7-11]` Routing is
by selector: the first 4 bytes of the proof pick the concrete verifier.
- `SP1VerifierGateway` routes SP1 proofs by leading selector (Groth16 route
`0x4388a21c`, Plonk `0x5a093a2f`).
- `RiscZeroVerifierRouter` routes a RISC Zero seal by leading selector
(`0xef6cb709` for 5.0.0-rc.1).
Which prover is chosen:
- **SP1 is the workhorse.** Every Aere application circuit routes through SP1:
zk-KYC screen, over-18, zkML MNIST, compliance pool, storage-proof coprocessor,
rollup validity anchor, and the recursive aggregator. The reason is concrete:
smallest proofs (~260 to 356 bytes), lowest typical verify gas (~300k), one Rust
guest toolchain, and native recursion via `verify_sp1_proof` inside the zkVM.
- **RISC Zero is the diversity path.** A second independent RISC-V zkVM, kept so
the verification surface is not single-prover; a real Groth16 factorization
receipt is recorded on-chain.
- **Halo2 / KZG is the direct-circuit path.** For circuits authored directly as a
PLONK arithmetization (bn254 / KZG / SHPLONK), plus the raw EIP-4844
point-evaluation precompile for blob and commitment openings.
The honest framing the source insists on: this is a multi-prover verification
surface behind one gateway, unusual to have in one place, but not a claim that no
other chain can verify any one of these.
### B.3 Why the on-chain-gas column motivates the STARK-verifier roadmap
SP1 Groth16, RISC Zero Groth16, and Halo2 all terminate their on-chain check in a
**BN254 pairing** (Groth16 or KZG). Two problems live in that column: the check
costs ~300k to ~450k gas, and BN254 pairing security rests on
discrete-log / pairing hardness, which is **Shor-breakable**. That single
elliptic-curve link is the only quantum-vulnerable step in an otherwise
hash-based SP1 proof chain.
Roadmap item: replace the BN254 Groth16 wrap with a direct hash-based STARK
verifier. `[CITED: aerenew/pqc-fork/pq-stark/README.md; aerenew/docs/PQ-STARK-VERIFIER-PRECOMPILE-2026-07-18.md]`
The design is a native precompile at `0x0AE8`. The generic verifier skeleton built
so far verifies a BabyBear / Plonky3 FRI STARK directly, with no BN254 wrap, so its
security rests only on hash collision-resistance and Reed-Solomon proximity gaps.
**Scope caveat (2026-07-19 research finding)** `[CITED: aerenew/docs/AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md]`:
the six generic components confirmed so far verify **BabyBear + FRI STARKs**
(Aere's OWN Plonky3 circuits), NOT SP1 6.1.0 proofs. The pinned SP1 6.1.0 is a
**Hypercube** release (KoalaBear multilinear: BaseFold plus sumcheck-zerocheck
plus LogUp-GKR), so its inner proof is hash-based but NOT a BabyBear FRI STARK, and
FRI plus DEEP-ALI do not apply to it. Replacing the SP1 BN254 Groth16 wrap on real
SP1 proofs is therefore a SEPARATE ~22 to 32 person-week retarget and a founder
decision, not a completion of the skeleton below.
**Honest status of that roadmap item, stated plainly** `[CITED: same README, "HONEST STATUS"]`:
it is a REFERENCE SKELETON plus DESIGN. It is NOT audited, NOT a working
verifier for SP1 proofs, and NOT activated on any Aere network. It fail-closes
(returns "not verified" for every input, cannot emit a false accept). The
SP1-recursion-specific crypto core is un-ported, and per the caveat above the
SP1 target is a different (Hypercube) proof system entirely. Mainnet 2800 has
exactly the five PQC precompiles `0x0AE1..0x0AE5`; `0x0AE8` is not live anywhere.
---
## PART C: CRYPTO-OP GAS (PQC vs ECDSA)
### C.1 Table (measured marginal precompile verify-op gas, with sources)
| Operation | Address | Marginal gas | Full verify-and-record tx gasUsed | Source |
|---|---|--:|--:|---|
| ECDSA (ecrecover) | 0x01 | ~3,000 | n/a | protocol constant `[VERIFY: G_ecrecover = 3000 is a fixed EVM constant, not Aere-specific]` |
| P-256 / passkey (RIP-7951, aka RIP-7212) | 0x100 | ~3,450 | n/a | `[CITED: aerenew/docs/wp2-sections/02-architecture.md:36; aerenew/research/aip-draft-pqc-precompiles.md section 4, "repo-sourced from the Fusaka notes"]` |
| SHAKE256 (FIPS 202) | 0x0AE5 | 60 base + 12/word | 21,470 | `[CITED: aerenew/pqc-fork/precompiles/HashToPointPrecompiledContract.java lines 51-52 (BASE_GAS=60, GAS_PER_WORD=12); aip-draft section 3 table]` |
| Falcon-512 (NIST round-3) | 0x0AE1 | 40,000 | 86,336 | `[CITED: aip-draft section 4 and Reference table]` |
| ML-DSA-44 (FIPS 204) | 0x0AE3 | 55,000 | 351,050 | `[CITED: aip-draft section 6 and Reference table]` |
| ML-KEM-768 (FIPS 203, encapsulation) | 0x0AE6 | 60,000 (fixed) | n/a (KEM, testnet-only) | `[CITED: aerenew/pqc-fork/precompiles/MLKEM768PrecompiledContract.java line 73 (GAS = 60_000)]` |
| Falcon-1024 (NIST round-3) | 0x0AE2 | 75,000 | 145,496 | `[CITED: aip-draft section 5 and Reference table]` |
| SLH-DSA-SHA2-128s (FIPS 205) | 0x0AE4 | 350,000 | 558,276 | `[CITED: aip-draft section 7 and Reference table]` |
All marginal-gas values are the scratch-fork (chain 28099) measured constants;
the verify-and-record tx gasUsed values are on-chain receipts. `[CITED: aerenew/research/aip-draft-pqc-precompiles.md Reference Implementation table]`
The five signature and hash precompiles (`0x0AE1..0x0AE5`) are live on mainnet
2800, activated at block 9,189,161. ML-KEM-768 (`0x0AE6`) and Falcon HashToPoint
(`0x0AE7`) are KAT-validated on an isolated testnet and remain founder-gated for
mainnet. `[CITED: aerenew/pqc-fork/README chain, pqc-fork memory]`
Scope reminder attached to this table: these precompiles are an application and
account layer capability. They do not make Aere consensus post-quantum.
### C.2 Two fresh cross-checks run this session
`[MEASURED-FRESH]` The SHAKE256 gas schedule (60 base plus 12 per word) is
confirmed present in source, and the HashToPoint precompile's own gas was freshly
computed from that exact source formula (`gasRequirement`, lines 78-93):
```
Falcon-512 (n=512) 32-byte message, 73-byte input: words=38 -> gasRequirement = 516
Falcon-1024 (n=1024) 32-byte message, 73-byte input: words=73 -> gasRequirement = 936
```
516 gas matches the source javadoc's "one ~500-gas staticcall" claim, which is
the mechanism by which HashToPoint cuts a full Solidity Falcon-512 verify. This
is the precompile's own intrinsic gas; it is distinct from the end-to-end verify
gas below, and the two must not be conflated.
`[CITED: aerenew/pqc-fork/results/bench-results.json]` The HashToPoint-precompile
optimization measured on the scratch fork (chain 28777), full Falcon-512 verify:
9,140,858 gas in pure EVM dropping to 7,186,367 gas with the precompile, a 21.4
percent reduction across 8 test vectors, all accept and tamper-reject checks
passing.
### C.3 The honest story the numbers tell
Post-quantum verification is materially more expensive than ECDSA. Ratios below
are fresh arithmetic from the cited marginal-gas constants divided by the
3,000-gas ecrecover:
| Scheme | Marginal gas | Cost vs ECDSA (3,000) |
|---|--:|--:|
| Falcon-512 | 40,000 | ~13.3x |
| ML-DSA-44 | 55,000 | ~18.3x |
| ML-KEM-768 | 60,000 | ~20.0x |
| Falcon-1024 | 75,000 | ~25.0x |
| SLH-DSA-SHA2-128s | 350,000 | ~116.7x |
So PQC verification runs roughly 13x (Falcon-512) to about 117x
(SLH-DSA-SHA2-128s) the cost of an ECDSA recover. That premium is exactly what
motivates two roadmap directions:
- **Aggregation.** Amortize one expensive PQC verify across many authentications
rather than paying per signature. `[VERIFY: the specific "AerePQAggregate"
contract name is not found in the repo; PQC aggregation is a documented roadmap
direction, see aerenew/docs/FRONTIER-PROGRAM-2026-07-12.md]`
- **Finality certificate.** Carry post-quantum finality as a certificate over a
finalized block rather than re-verifying PQC signatures per transaction. This
ties to the dual-quorum PQ-consensus design work. `[CITED: aerenew/docs/PQ-CONSENSUS-STEP2-2026-07-18.md]`
Note the scope line: this is design and attestation alongside QBFT, it does not
make consensus post-quantum.
---
## PART D: INDEPENDENT REPRODUCTION
### D.1 The strongest benchmarks should be third-party reproduced
The credibility of every number above rises sharply when a party other than Aere
reruns it. The honest reproduction candidates, in priority order:
- **The Nethermind team (warmest angle).** Aere already runs a patched Nethermind
as a second client that validates and follows live chain 2800, and it runs the
PQC precompiles and can produce QBFT blocks Besu accepts. `[CITED: aerenew/docs/wp2-sections/09-governance-roadmap.md:199, 253; NETHERMIND-2ND-CLIENT-LIVE memory]`
That existing relationship makes them the natural first ask for an independent
client-side reproduction of the precompile gas and the consensus behavior.
- **A university lab.** Cheap under a research grant and citable in a way a vendor
benchmark is not; a good fit for the Block-STM correctness-and-scaling harness
and the zkVM proving-time measurements, which are self-contained and need no
Aere infrastructure.
- **Explicitly NOT the expensive audit firms.** The audit-firm budget is reserved
for the security audit, which is a different deliverable from a performance
reproduction. `[CITED: aerenew/docs/mainnet_authority context; AUDIT-OUTREACH-2026-07-12.md]`
The outreach kit is prepared by us. The actual third-party run and the
relationship are founder and business gated, not something this report initiates.
### D.2 The run-it-yourself manifest (REPRODUCE)
This report is the NUMBERS and how they were measured. The run-it-yourself
commands live in the reproducibility bundle. Today that bundle is split across
several committed files rather than one consolidated `REPRODUCE.md`; the honest
recommendation is to consolidate them, but each is real and runnable now:
| Domain | Where to run it | File |
|---|---|---|
| Block-STM correctness + scaling (Part A.3, freshly run here) | `cd aerenew/parallel-executor && cargo test --release && cargo run --release -- bench` and `-- endtoend` | `aerenew/parallel-executor/BLOCKSTM-ENDTOEND-THROUGHPUT-2026-07-13.md` section 10 |
| PQC precompile KATs + gas (Part C) | `aerenew/pqc-fork/run-kats.sh`, `aerenew/pqc-fork/bench/bench.py` | `aerenew/pqc-fork/` (results in `results/bench-results.json`, `results/kat-results-*.json`) |
| Reproducible Besu client build | `docker build ... -f aerenew/node/Dockerfile.reproducible` then `aerenew/scripts/verify-besu-image.sh` | `aerenew/node/REPRODUCIBLE.md` (note: public digest manifest publication is still roadmap, so the third-party comparison step is not yet live) |
| PQ-consensus reproduction | committed harness | `aerenew/research-stage/formal-consensus/REPRODUCE.md`, `aerenew/audit-package-pq-consensus/REPRODUCE.md` |
`[MEASURE: create a single top-level aerenew/REPRODUCE.md that indexes all four
domains above with pinned commands, so a third party has one entry point]`
---
## Appendix: provenance summary of every number
**Freshly measured this session `[MEASURED-FRESH]`:**
- Block-STM `parallel==sequential` gate: PASS on all 4 conflict profiles (this Windows box).
- Block-STM 16-thread execution-phase speedup this box: low 4.36x, medium 4.96x, high 1.92x, pathological 0.43x.
- HashToPoint precompile intrinsic gas from source formula: 516 gas (Falcon-512, 32-byte message), 936 gas (Falcon-1024).
**Cited from committed repo artifacts `[CITED]`:**
- 273,000 TPS is a design ceiling, never measured (WHITEPAPER-V2, wp2-sections).
- 0.5 s block period, halved at block 2,138,451, single-slot QBFT finality (wp2-sections, genesis-subsecond.json).
- Rollup end-to-end harness ~247k to 270k TPS at gas=80, swinging 115k to 500k, not the L1 ceiling (BLOCKSTM-ENDTOEND section 7).
- Reference 16-core Block-STM ~8.3x to 9.4x exec, ~8.3x to 9.1x end-to-end (same artifact).
- SP1 Groth16 ~300k gas, ~260 to 356 byte proofs; measured records storage-proof 348,345, zkML 326,492, aggregation ~390k (spec-zk-stack; deployment artifacts).
- SP1 proving times: zkscreen ~72 to 75 s, over18 ~71 to 73 s, zkml-mnist ~200 to 205 s, aggregation Groth16 526.1 s (proof-aggregator-scale.json).
- RISC Zero Groth16 seal 260 bytes, 5.0.0-rc.1 (risc0-verifier-fix.json).
- Halo2 ~449,517 gas, 1,152-byte proof; KZG point-eval ~255,719 gas (deployment artifacts, spec-zk-stack).
- PQC marginal gas: Falcon-512 40,000, Falcon-1024 75,000, ML-DSA-44 55,000, SLH-DSA-128s 350,000, ML-KEM-768 60,000, SHAKE256 60+12/word; verify-and-record tx: 86,336 / 145,496 / 351,050 / 558,276 / 21,470 (aip-draft, precompile source, bench-results.json).
- P-256 ~3,450 gas (wp2-sections, aip-draft).
- HashToPoint optimization 9,140,858 to 7,186,367 gas, -21.4% (bench-results.json).
**To measure `[MEASURE]` or verify `[VERIFY]` (open items):**
- `[MEASURE]` Observed live mainnet sustained TPS over a real block window.
- `[MEASURE]` SP1 Plonk proving time, gas, proof size.
- `[MEASURE]` RISC Zero on-chain verify gas and proving time.
- `[MEASURE]` Halo2 proving time.
- `[MEASURE]` Wall-clock verification latency per prover.
- `[MEASURE]` Consolidated top-level aerenew/REPRODUCE.md.
- `[VERIFY]` The explicit block-space arithmetic behind 273,000 TPS.
- `[VERIFY]` Which genesis gas limit is live on 2800 (40M vs effectively unbounded).
- `[VERIFY]` ECDSA ecrecover 3,000 is a fixed protocol constant (not Aere-specific).
- `[VERIFY]` The "AerePQAggregate" contract name (not found in repo; aggregation is a documented roadmap direction).

View File

@ -0,0 +1,80 @@
# Aere Network: Autonomous Build Loop Summary (2026-07-19)
A single clean record of the non-stop build loop. Every item below was verified before it was marked done: real numbers where measured, honest `[MEASURE]` / `[VERIFY]` markers where not, and the post-quantum scope boundary kept everywhere (consensus is classical secp256k1 ECDSA QBFT; only signatures, accounts, and precompiles are post-quantum). Nothing here was deployed to mainnet; every activation stays founder-gated.
## Consolidation QA (proof it all coexists)
- Full rebuild: `hardhat clean && compile` compiled 339 Solidity files, zero errors, all new contracts present.
- Tests: 118 passing, 1 pending (a deliberate not-applicable case), 0 failing across the 10 new test files.
- Cross-document consistency: 0 contradictions. Burn is 37.5% of the validator reward (cap 50%, not a base-fee burn); live precompiles are 0x0AE1..0x0AE5; consensus is N=7 / f=2 / quorum 5; the 273k TPS figure is labeled a ceiling, never a measured rate.
- Report: `aerenew/docs/AERE-QA-CONSOLIDATION-REPORT.md`.
## Depth verification phase (after the roadmap breadth was built)
The new contracts were not just built and unit-tested; they were then reviewed, hardened, and formally proven:
- Adversarial security review (17 contracts, fund-flow first): 0 CRITICAL, 0 HIGH, 1 MEDIUM, 3 LOW. The 4 money contracts are clean on all priority properties (CEI plus reentrancy, per-asset escrow solvency with no double-count, no double-pay, no pay-without-proof, fee-on-transfer rejected, fail-closed PQC consumption). Report: `AERE-SECURITY-REVIEW-NEW-CONTRACTS.md`.
- All findings fixed and re-tested: M1 (feedback-author allowlist), L1 (codeless-gateway guard in 3 verifiers), L2 (VectorStore receipt bound to provider), L3 (documented, executor-gated). Report: `AERE-SECURITY-FIXES.md`.
- Formal verification broadened from the existing consensus corpus to the new contracts: 7 new z3 SMT models (3 fund-flow, 4 registry/verifier), each proved with a firing negative control (non-vacuous), wired into the canonical runner. Fresh canonical run: 15 models, 15 of 15 pass, 128 properties PROVED, 79 counterexample negative controls fired, 0 failed. Reports: `AERE-FORMAL-EXTENSION-MONEY-CONTRACTS.md`, `AERE-FORMAL-BROADEN-REPORT.md`; `aerenew/REPRODUCE.md` updated with the real fresh count.
- Full regression after the security fixes: compile green, 122 tests passing, 0 failing, 1 pending (a deliberate not-applicable case). No regression.
Net state of the new contract suite: built, unit-tested (122 passing), QA-compiled (339 files), security-reviewed and hardened (0 critical/high open), and formally proven (15 z3 models). All founder-gated, none deployed.
## Wave 1: the credibility document cluster (13 docs, the "grade 10" package)
All under `aerenew/docs/` (plus `aerenew/REPRODUCE.md` and `aerenew/formal-tla/`).
| # | Deliverable | File |
|---|---|---|
| 17 | Engineering and security specification (threat model, guarantees table, 11 assumptions, design rationale, failure analysis, engineering principles) | AERE-ENGINEERING-SECURITY-SPEC.md |
| 22 | Architecture map (layered stack plus cross-cutting concerns, Mermaid) | AERE-ARCHITECTURE-MAP.md |
| 20 | Ethereum EIP compatibility matrix (EIP by EIP, plus deliberate deviations) | AERE-EIP-COMPATIBILITY-MATRIX.md |
| 19 | NIST validation status (ACVP for FIPS-final schemes, KAT for Falcon, honest "not a certification") | AERE-NIST-VALIDATION-STATUS.md |
| 18 | Reproducible benchmark report (chain, zkVM comparative, crypto-op gas; fresh Block-STM run) | AERE-BENCHMARK-REPORT.md |
| 23 | Reproducibility manifest (fresh SMT 10/10 + 8/8, contract SMT 9/9 green, Hardhat spot-check) | aerenew/REPRODUCE.md |
| 25 | AIP / RFC process and index (real AIP-1..8 inventory, drafted AIP-9..16, no fabricated discussion) | AERE-AIP-PROCESS-AND-INDEX.md |
| 24 | Protocol specification (the delta over Ethereum + QBFT, reimplementable) | AERE-PROTOCOL-SPECIFICATION.md |
| 26 | Cross-client determinism (Besu vs Nethermind byte-identical evidence) | AERE-CROSS-CLIENT-DETERMINISM.md |
| 28 | Reproducible builds and deterministic release | AERE-REPRODUCIBLE-BUILDS.md |
| 29 | Post-quantum governance design | AERE-POST-QUANTUM-GOVERNANCE.md |
| 30 | Quantum-safe networking (transport layer, PQ TLS + devp2p) | AERE-QUANTUM-SAFE-NETWORKING.md |
## New contracts built and tested (all founder-gated, none deployed)
| # | Contract(s) | Tests | Notes |
|---|---|---|---|
| 9 | AereRandomnessBeaconV2 (oracle) | 15 passing | Real BLS12-381 drand verify via EIP-2537, replaces a `return true` stub, fail-closed. Ran against live drand quicknet vectors. |
| 3 | AereRecoveryRegistry | 10 passing | Falcon-attested consensus-recovery registry + chaos matrix scaffold + watchtower design. |
| 5 | erc8004/ (Identity, Reputation, Validation, AP2 mandate) | 18 passing | Thin adapters over the live agent stack; PQC validation registry is the differentiator. |
| 7 | compliance/ (TrustRegistry, VerifiableCredential, BitstringStatusList) | 12 passing | Institution-grade compliance passport, W3C VC 2.0 + eIDAS-aligned trust registry + zk non-revocation interface. |
| 8 | intents/AereDestinationSettler | 4 passing | ERC-7683 destination fill, wired to the QBFT finality light client for repayment. |
| 10 | depin/AereComputeMarketV3 | 13 passing | Proof-carrying compute jobs (replay / zk-verified / optimistic) + PQC settlement + escrow solvency invariant. |
| 12 | agentic/AereVectorStore | 8 passing | Verifiable semantic memory for AI agents (Falcon-signed commits, attested retrieval, AERE402 paid query). |
| 14 | pqc/AereAccountMigrator | 9 passing | Atomic ECDSA-account to PQC-account migration (the last account-layer quantum gap). |
| 6 | mpc/AerePQAggregateVerifier + modular/AerePQAggregateModule | 17 passing | One SP1 proof attests a t-of-n PQC committee, cost O(1) in n (measured 61.8k gas flat), ERC-7579 portable. |
| 4 | pqfinality/AerePQAttestationKeyRegistry + AereFinalityCertificateVerifier | 12 passing | Additive post-quantum finality certificate; no consensus flip, no re-genesis. |
For every contract above, the Falcon precompile (0x0AE1) and the SP1 gateway are mocked in the unit tests and hard-wired to the live addresses for production; the real-precompile path is proven separately against live mainnet.
## Formal methods, research, and tooling
| # | Deliverable | Status |
|---|---|---|
| 21 | Certora CVL formal specs for burn / sAERE / registries / threshold | Written; running the prover is `[MEASURE]` (Certora free tier). No rule claimed proved. |
| 27 | TLA+ specification of QBFT (safety + liveness, N=4 and N=7 configs) | Written and structurally checked; running TLC is `[MEASURE]`. Complements the z3 corpus. |
| 2 | Native STARK-verify (0x0AE8) porting spec + generic BabyBear+FRI components | Honest: a ~11-16 person-week port. The six generic components (BabyBear field, Poseidon2, MMCS, FRI, transcript, generic AIR quotient) are conformance-confirmed against Plonky3 `0.4.3-succinct`. SCOPE CAVEAT (2026-07-19 finding): those components verify BabyBear+FRI STARKs (Aere's OWN Plonky3 zk-circuits), NOT SP1 6.1.0, which is Hypercube (KoalaBear multilinear); FRI/DEEP-ALI do not apply to SP1 6.1.0. This does NOT let us "verify SP1 proofs now"; replacing the SP1 BN254 Groth16 wrap is a separate ~22-32 person-week retarget (`AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`). Top-level verifier stays fail-closed and verifies nothing. |
| 31 | Quantum Migration Toolkit (scanner + SDK + cost simulator) | Runnable tool. Scanner ran against live RPC (chain 2800). `aerenew/pqc-migration-toolkit/`. |
## Off-roadmap issues caught and flagged (spawned as separate tasks)
- A stale AIP copy in `aere-research-repo/aips/` still claims "three validators" and a Foundation "multisig", contradicting the current seven-validator, single-key reality. Flagged for fix-to-honest.
- The `Dockerfile.reproducible` base-image digests are placeholders, so the reproducible build cannot resolve until real registry digests are pinned. Flagged.
## What remains gated (needs you, not code)
- External audit (Trail of Bits / NCC Group): the single largest credibility gate, business-funded.
- Consensus post-quantum activation, first non-Foundation validator, contract-ownership transfer to the Timelock, Season 1 funding, bug-bounty funding: all founder-gated and, where relevant, audit-gated and N greater-than-or-equal-to 9 plus soak gated.
- First live CoreBook market, institutional compliance adoption, real GPU/prover supply: business and counterparty gated.
- Deferred by founder decision (queued, not run): encrypted mempool (#15), economic-soundness simulation (#16).
Nothing in this loop moved real funds, created a token, or touched the live validators or the production infra box.

View File

@ -0,0 +1,821 @@
# Spec 21: Certora formal-verification specifications for Aere Network's safety-critical contracts
Status date: 2026-07-19. Author: Aere Network engineering (self-authored CVL).
## HONEST STATUS BANNER (read first)
This document contains **written-and-ready** Certora Verification Language (CVL) specifications. It does
**not** contain proof results. Every CVL rule and invariant here is authored against the real contract
source in this repository, but **none has been executed by the Certora Prover**, so **nothing in this
document is claimed "proved."**
Running these specs requires the Certora Prover, which is an external tool:
- Free tier available at `prover.certora.com` (needs an account).
- Local driver is the `certora-cli` Python package (`pip install certora-cli`), which uploads the job
to Certora's cloud solver back end.
- The prover is **not installed or run in this environment.**
Wherever a proof result would go, this document writes a `[MEASURE]` marker naming the exact command to
run and the artifact to publish. A rule graduates from "written" to "proved" only after `certoraRun`
returns `Verified` for it and the HTML rule report is published. Until then the correct description is
**"CVL authored, not yet run."**
Scope boundary preserved throughout: these are **application / account / tokenomics-layer** contracts.
Aere Network consensus remains classical ECDSA QBFT (Besu). No rule here asserts post-quantum consensus;
the PQC content is signature verification and key custody at the contract layer only.
---
## 1. Why Certora, and how it complements the existing formal work
Aere already runs two layers of machine-checked reasoning:
| Layer | Tool | What it checks | Where it lives |
|---|---|---|---|
| Design-level SMT | z3 (Python) | Property holds over an abstract model of the contract logic (guards, arithmetic, status machine) | `aerenew/formal-consensus/*_smt.py` |
| Symbolic bytecode | Halmos / forge | Property holds over the compiled EVM bytecode for symbolic inputs, bounded | `aerenew/formal-contracts/specs/*.symbolic.t.sol`, `aerenew/contracts/test/formal/*.symbolic.t.sol` |
| Property fuzzing | Foundry / Medusa | Property survives large random input campaigns (sampling) | `aerenew/contracts/test/**` |
Fuzzing **samples** the input and state space. It runs a property against many concrete inputs and
reports the ones it happened to try. A clean fuzz run is strong evidence, but it is evidence about the
paths that were sampled, not a statement about the paths that were not.
The Certora Prover is different in kind. It compiles the contract to a logical transition system and asks
an SMT solver whether the negation of a rule is satisfiable **over all inputs and all reachable states**
(subject to declared loop bounds and summaries). A rule that verifies is a statement that no input, in no
ordering, from no reachable state, can violate the property. A rule that fails returns a concrete
counterexample transaction.
Certora sits above the existing work rather than replacing it:
- It shares the z3 models' **property statements** (the "what to prove"), which is why this document
reuses the invariants already written in `threshold_account_smt.py`, `cryptoregistry_smt.py`, and
`pqckeyregistry_smt.py` verbatim as the source of truth, now expressed in CVL.
- Unlike the z3 models, which reason over a hand-built abstraction, CVL rules are checked against the
**actual compiled contract**, closing the gap the z3 files honestly flag as their own boundary
("a design model, not the Solidity bytecode").
- Unlike Halmos, which is symbolic-execution based and bounded per call, the Certora Prover reasons
inductively across an arbitrary method sequence via `invariant` and parametric `rule` constructs, and
gives first-class support for `ghost` state and storage `hook`s.
The three tools are complementary: z3 fixes the property at design time, Halmos and Certora both check it
against bytecode (Halmos by bounded symbolic execution, Certora by inductive SMT over all methods), and
fuzzing provides cheap continuous regression coverage.
---
## 2. Target contracts and the properties to prove
Each subsection: (1) the real interface as read from source, with file path and the exact state
variables / function signatures the CVL binds to, then (2) the CVL rules and invariants.
Convention in the CVL: `env e` is the calling environment, `mathint` is the unbounded integer sort (used
to make arithmetic assertions overflow-free), `currentContract` is the contract under verification, and
`nativeBalances[a]` is the native-AERE balance of address `a`. A `using X as y` line links a second
contract (for example the ERC-20 asset) so its storage is observable inside a rule.
---
### 2.a Burn stack
#### 2.a.1 `AereSink` (immutable 3-bucket router)
Source: `aerenew/contracts/contracts/sink/AereSink.sol`. Read confirms:
- `is ReentrancyGuard` only. **No `Ownable`, no admin role, no setters** (`grep` for `onlyOwner`,
`setBucket`, `setRecipient` returns nothing; the NatSpec asserts this and the source matches).
- Immutable state (each exposed by an auto getter): `address AERE`, `address BURN_VAULT`,
`address SAERE_VAULT`, `address DEX_ROUTER`, `uint16 BURN_BPS`, `uint16 BUYBACK_BPS`,
`uint16 STAKER_YIELD_BPS`, `uint16 MAX_SLIPPAGE_BPS`, `address ORACLE`.
- Constructor reverts with `BpsMustSumTo10000` unless `BURN_BPS + BUYBACK_BPS + STAKER_YIELD_BPS == 10000`.
- Mutating entrypoints: `flush(address token, uint256 amount)`, `sweepDust(address token)`. In the
`token == AERE` branch both dispatch only to `BURN_VAULT` and `SAERE_VAULT` via `_safeTransfer`; the
`token != AERE` branch routes the same two addresses through `_swapAndForward`, where `recipient` is
only ever `BURN_VAULT` or `SAERE_VAULT` (proven set by inspection).
Properties (matching the z3 / Halmos SK-series and the SECURITY-POSTURE "no path sends to an arbitrary
address" statement):
```cvl
// certora/specs/AereSink.spec
using DummyERC20A as aere; // the AERE / WAERE asset, linked so its balances are observable
methods {
function BURN_BPS() external returns (uint16) envfree;
function BUYBACK_BPS() external returns (uint16) envfree;
function STAKER_YIELD_BPS() external returns (uint16) envfree;
function BURN_VAULT() external returns (address) envfree;
function SAERE_VAULT() external returns (address) envfree;
function DEX_ROUTER() external returns (address) envfree;
function AERE() external returns (address) envfree;
function aere.balanceOf(address) external returns (uint256) envfree;
}
// SK-INV-1 the three bucket splits always sum to exactly 10000 basis points.
// Immutable, so the constructor's BpsMustSumTo10000 guard is the whole proof; the
// invariant asserts no reachable state breaks it.
invariant bpsSumTo10000()
to_mathint(BURN_BPS()) + to_mathint(BUYBACK_BPS()) + to_mathint(STAKER_YIELD_BPS()) == 10000;
// SK-INV-2 the bucket recipients can never be redirected: no method changes the
// immutable BURN_VAULT / SAERE_VAULT addresses. This is the "no owner/admin can
// redirect funds" property expressed as an all-methods parametric rule.
rule recipientsImmutable(method f, env e, calldataarg args) {
address burnBefore = BURN_VAULT();
address saereBefore = SAERE_VAULT();
f(e, args);
assert BURN_VAULT() == burnBefore, "BURN_VAULT is immutable";
assert SAERE_VAULT() == saereBefore, "SAERE_VAULT is immutable";
}
// SK-RULE-3 NO ARBITRARY RECIPIENT: a flush of AERE never increases the AERE
// balance of any address that is not one of the fixed buckets, the router, or the
// sink itself. There is no code path that pays a caller-chosen address.
rule noFundsToArbitraryAddress(env e, uint256 amount, address stranger) {
require stranger != BURN_VAULT();
require stranger != SAERE_VAULT();
require stranger != DEX_ROUTER();
require stranger != currentContract;
mathint before = aere.balanceOf(stranger);
flush(e, AERE(), amount);
mathint after = aere.balanceOf(stranger);
assert after <= before,
"flush must never credit an address outside the immutable bucket set";
}
// SK-RULE-4 same guarantee for the permissionless sweepDust path.
rule sweepDustNoArbitraryAddress(env e, address stranger) {
require stranger != BURN_VAULT();
require stranger != SAERE_VAULT();
require stranger != DEX_ROUTER();
require stranger != currentContract;
mathint before = aere.balanceOf(stranger);
sweepDust(e, AERE());
assert aere.balanceOf(stranger) <= before;
}
```
Note on the token != AERE branch: `_swapAndForward` calls out to `DEX_ROUTER`, an external address.
Certora leaves the router call to its default havoc, which conservatively allows the router to move funds
however it likes; the CVL above therefore restricts SK-RULE-3/4 to the `token == AERE` direct-settle
branch (no external callback), exactly the branch the Halmos SK-series covers. The router branch is
argued by inspection (identical `recipient` set) and is a `[MEASURE]` extension: add a `DISPATCHER`
summary for `IAereV2Router.swapExactTokensForTokens` that forwards only to `recipient`.
#### 2.a.2 `AereCoinbaseSplitterV2`
Source: `aerenew/contracts/contracts/AereCoinbaseSplitterV2.sol`. Read confirms:
- `is Ownable, ReentrancyGuard`. Mutable `uint256 burnBps` (default 3750), `uint256 sinkBps` (default 1500).
- Constants: `BPS = 10000`, `MAX_BURN_BPS = 5000`, `MAX_SINK_BPS = 3000`, `SINK_CHANGE_TIMELOCK = 7 days`.
- `setBps(uint256 _burnBps, uint256 _sinkBps)` is `onlyOwner` and reverts `BpsOutOfRange` unless
`_burnBps <= MAX_BURN_BPS`, `_sinkBps <= MAX_SINK_BPS`, and `_burnBps + _sinkBps <= BPS`.
- `rebateBps()` view returns `BPS - burnBps - sinkBps` (derived, so it must stay non-negative).
```cvl
// certora/specs/AereCoinbaseSplitterV2.spec
methods {
function burnBps() external returns (uint256) envfree;
function sinkBps() external returns (uint256) envfree;
function BPS() external returns (uint256) envfree;
function MAX_BURN_BPS() external returns (uint256) envfree;
function MAX_SINK_BPS() external returns (uint256) envfree;
function owner() external returns (address) envfree;
}
// CS-INV-1 burnBps is capped at MAX_BURN_BPS (5000) in every reachable state.
// This is the on-chain form of "validator-reward burn cut, hard-capped at 50%".
invariant burnBpsWithinCap()
burnBps() <= MAX_BURN_BPS();
// CS-INV-2 sinkBps is capped at MAX_SINK_BPS (3000).
invariant sinkBpsWithinCap()
sinkBps() <= MAX_SINK_BPS();
// CS-INV-3 burn + sink never exceed the total, so the derived rebateBps
// (BPS - burnBps - sinkBps) can never underflow / go negative.
invariant burnPlusSinkWithinTotal()
to_mathint(burnBps()) + to_mathint(sinkBps()) <= to_mathint(BPS());
// CS-RULE-4 only the owner can move the bps, and only within the caps. Any method
// that changes burnBps or sinkBps must be called by the owner and must land inside
// the caps (so no path, owner or not, exceeds MAX_BURN_BPS).
rule onlyOwnerChangesBpsWithinCaps(method f, env e, calldataarg args) {
uint256 b0 = burnBps();
uint256 s0 = sinkBps();
f(e, args);
bool changed = (burnBps() != b0) || (sinkBps() != s0);
assert changed => e.msg.sender == owner(), "only owner may change bps";
assert burnBps() <= MAX_BURN_BPS(), "post-state respects the burn cap";
assert to_mathint(burnBps()) + to_mathint(sinkBps()) <= to_mathint(BPS());
}
```
#### 2.a.3 `AereFeeBurnVault`
Source: `aerenew/contracts/contracts/AereFeeBurnVault.sol`. Read confirms:
- No `Ownable`, no admin. Functions: `receive()`, `burn()` (payable), `burnToken(address,uint256)`,
`sweepToZero()`, `currentAEREBalance()` view. **There is no `withdraw`, no `rescue`, no admin escape.**
- `uint256 totalBurnedAERE` increments on every native inflow; `uint256 totalSentToZero` increments in
`sweepToZero`; `mapping(address => uint256) totalBurnedToken`.
- `sweepToZero()` sends the whole native balance to `address(0)` (the burn sink) and to nowhere else.
```cvl
// certora/specs/AereFeeBurnVault.spec
methods {
function totalBurnedAERE() external returns (uint256) envfree;
function totalSentToZero() external returns (uint256) envfree;
}
// FB-INV-1 the lifetime burn counter is monotonic non-decreasing (it is a running
// total of arrivals; no path decrements it).
rule totalBurnedMonotonic(method f, env e, calldataarg args) {
mathint before = totalBurnedAERE();
f(e, args);
assert to_mathint(totalBurnedAERE()) >= before;
}
// FB-RULE-2 NO WITHDRAW PATH: the vault's own native balance is monotonic
// non-decreasing for EVERY method except sweepToZero. sweepToZero is the only
// method that can reduce the balance, and (FB-RULE-3) it can only send to the burn
// sink (address(0)), never to a caller-chosen address.
rule balanceMonotonicExceptBurnSink(method f, env e, calldataarg args)
filtered { f -> f.selector != sig:sweepToZero().selector } {
mathint before = nativeBalances[currentContract];
f(e, args);
assert nativeBalances[currentContract] >= before,
"no non-sweep method may reduce the vault balance (there is no withdraw)";
}
// FB-RULE-3 the only sink is address(0): sweepToZero credits no non-zero address.
// (address(0) is the burn destination; every other account's balance is untouched
// or reduced, never increased by the vault's operation.)
rule sweepGoesOnlyToBurnSink(env e, address r) {
require r != 0; // any real, non-burn address
require r != currentContract;
mathint before = nativeBalances[r];
sweepToZero(e);
assert nativeBalances[r] <= before,
"sweepToZero must not credit any address other than the address(0) burn sink";
}
```
`[MEASURE: run certoraRun certora/conf/burnstack.conf and publish the AereSink / AereCoinbaseSplitterV2
/ AereFeeBurnVault rule report]`
---
### 2.b Tokenomics: `sAERE` (ERC-4626 staking receipt)
Target the corrected `sAEREv2`. Source: `aerenew/contracts/contracts/staking/sAEREv2.sol` (the R7-fixed
redeploy; the live `sAERE` at `0xA212...50b0` shares the same interface but not the R7 fix, see the note
on SR-RULE-3). Read confirms:
- `is ERC4626`. **No admin / owner / pause / upgrade proxy / parameter setters.** Constants
`DRIP_DURATION = 7 days`, `DRIP_RESTART_THRESHOLD_BPS = 100`, `DEAD_SHARES_SEED_WAERE = 1000 wei`,
`_decimalsOffset() == 6`.
- State: `uint256 rewardRate`, `uint256 periodFinish`, `uint256 lastObservedBalance`.
- `totalAssets()` returns `syncedBalance - undistributed`, where `syncedBalance = min(assetBalance,
lastObservedBalance)` (the R7 fix) and `undistributed = _undistributedNow()`.
- `sync()` is permissionless and only observes arrivals; it never moves funds out. Public `deposit`,
`mint`, `withdraw`, `redeem` each call `sync()` first, then the OZ super.
Properties (donation-inflation resistance, totalAssets consistency, exchange-rate monotonicity under the
drip):
```cvl
// certora/specs/sAEREv2.spec
using DummyERC20A as waere; // the underlying WAERE asset
methods {
function totalSupply() external returns (uint256) envfree;
function totalAssets() external returns (uint256) envfree;
function undistributedRewards() external returns (uint256) envfree;
function convertToAssets(uint256) external returns (uint256) envfree;
function asset() external returns (address) envfree;
function waere.balanceOf(address) external returns (uint256) envfree;
}
// SR-RULE-1 DONATION / INFLATION RESISTANCE: no bare transfer into the vault, and
// no sync(), mints shares. Share supply can increase ONLY through deposit or mint.
// A raw WAERE.transfer to the vault (the classic ERC-4626 first-depositor inflation
// donation) leaves totalSupply untouched.
rule noShareMintOnDonationOrSync(method f, env e, calldataarg args)
filtered { f -> f.selector != sig:deposit(uint256,address).selector
&& f.selector != sig:mint(uint256,address).selector } {
mathint tsBefore = totalSupply();
f(e, args);
assert to_mathint(totalSupply()) <= tsBefore,
"only deposit / mint may increase sAERE share supply";
}
// SR-INV-2 totalAssets CONSISTENCY: reported assets never exceed the vault's real
// WAERE balance (the R7 fix subtracts the undistributed reserve from the SYNCED
// balance, so an un-synced donation is never counted early and totalAssets is a
// true lower bound on redeemable backing).
invariant totalAssetsNeverExceedsBalance()
to_mathint(totalAssets()) <= to_mathint(waere.balanceOf(currentContract));
// SR-INV-3 the unvested drip reserve is fully backed: undistributedRewards never
// exceeds the real balance (so the reserve can never be double-counted into a
// payout, the exact R7 defect that drove totalAssets to 0 in v1).
invariant reserveFullyBacked()
to_mathint(undistributedRewards()) <= to_mathint(waere.balanceOf(currentContract));
// SR-RULE-4 EXCHANGE-RATE MONOTONICITY UNDER THE DRIP: calling sync() never lowers
// the value of a share. With share supply unchanged across the call, the assets a
// fixed share amount converts to does not fall. In v2 an arrival folded into the
// reserve is rate-neutral at the sync instant (R7) and then vests upward over time;
// it never steps down. Running this same rule against the pre-R7 sAERE.sol produces
// a counterexample (v1 counts the arrival in totalAssets first, so sync() drops the
// rate): that CEX is the R7 bug, which is why sAEREv2 is the verification target.
rule syncNeverLowersShareValue(env e, uint256 shareAmt) {
require shareAmt > 0;
mathint supplyBefore = totalSupply();
mathint assetsPerShareBefore = convertToAssets(shareAmt);
sync(e);
assert to_mathint(totalSupply()) == supplyBefore, "sync mints/burns no shares";
assert to_mathint(convertToAssets(shareAmt)) >= assetsPerShareBefore,
"a share is never worth less after sync (drip is up-only)";
}
```
`[MEASURE: run certoraRun certora/conf/sAEREv2.conf and publish the rule report]`. Note the deliberate
demonstration value of SR-RULE-4: it is written to PASS on `sAEREv2.sol` and to FAIL (with a printed
counterexample) on the un-fixed `sAERE.sol`, matching the R7 finding in
`SECURITY-POSTURE-VERIFICATION-2026-07-15.md`. Do not claim either result until the prover has run both.
---
### 2.c Registries
#### 2.c.1 `AereCryptoRegistry`
Source: `aerenew/contracts/contracts/pqc/AereCryptoRegistry.sol`. Read confirms:
- `is Ownable`. Storage: `mapping(uint256 => Algorithm) _algorithms`, `uint256 _count`, `bool _seeded`.
Ids are 1-based and monotonic (`_count += 1` in `_addAlgorithm`, never decremented).
- `Algorithm` fields set once at `addAlgorithm` and never rewritten afterward: `name`, `scheme`,
`verifier`, `pubKeyLen`, `sigLen`, `esigHeader`, `addedBlock`, `isSignature`. Only `status`
(`setStatus`) and `successorId` (`setSuccessor`) are ever mutated post-registration.
- `enum Status { UNKNOWN, ACTIVE, DEPRECATED, REVOKED }`. `verify(...)` is fail-closed: returns false
(never reverts) for a non-existent id, a hash-only row (`isSignature == false`), or a row whose status
is `UNKNOWN` or `REVOKED`, BEFORE the `a.verifier.staticcall`.
Properties mirror `cryptoregistry_smt.py` (R1 fail-closed, plus the append-only / no-re-point statements
required by the task):
```cvl
// certora/specs/AereCryptoRegistry.spec
methods {
function algorithmCount() external returns (uint256) envfree;
function statusOf(uint256) external returns (AereCryptoRegistry.Status) envfree;
function owner() external returns (address) envfree;
// verify staticcalls an external verifier; leave it to the prover's default
// havoc so the fail-closed rules must hold for ANY verifier answer.
}
// CR-INV-1 algorithm ids are append-only: the count never decreases, so an id that
// exists can never be un-registered (rows are added, never removed).
rule algorithmCountMonotonic(method f, env e, calldataarg args) {
mathint before = algorithmCount();
f(e, args);
assert to_mathint(algorithmCount()) >= before;
}
// CR-RULE-2 NO UNAUTHORIZED RE-POINT: an existing row's verifier address is
// immutable. Once a scheme id maps to a verifier, no method (setStatus,
// setSuccessor, addAlgorithm of a later id, seedLiveSchemes) can silently rewrite
// it. To change a verifier you must add a NEW id; the old id -> verifier binding is
// write-once. This is the "recorded entries are append-only, cannot be silently
// rewritten" property.
rule verifierNeverRepointed(method f, env e, calldataarg args, uint256 id) {
require id != 0 && id <= algorithmCount();
AereCryptoRegistry.Algorithm a0 = getAlgorithm(e, id);
f(e, args);
require id <= algorithmCount();
AereCryptoRegistry.Algorithm a1 = getAlgorithm(e, id);
assert a1.verifier == a0.verifier, "verifier address is write-once";
assert a1.scheme == a0.scheme, "scheme tag is write-once";
assert a1.pubKeyLen == a0.pubKeyLen, "pubKeyLen is write-once";
assert a1.sigLen == a0.sigLen, "sigLen is write-once";
assert a1.esigHeader == a0.esigHeader, "wire-format header is write-once";
assert a1.isSignature == a0.isSignature, "signature/hash flag is write-once";
assert a1.addedBlock == a0.addedBlock, "addedBlock is write-once";
}
// CR-RULE-3 only the owner can mutate the registry (add / setStatus / setSuccessor
// / seed). The onlyOwner modifier reverts otherwise, so a completed mutating call
// implies the caller was the owner.
rule onlyOwnerMutates(method f, env e, calldataarg args)
filtered { f -> f.selector == sig:addAlgorithm(string,uint8,address,uint16,uint32,uint8,uint256,bool).selector
|| f.selector == sig:setStatus(uint256,AereCryptoRegistry.Status).selector
|| f.selector == sig:setSuccessor(uint256,uint256).selector
|| f.selector == sig:seedLiveSchemes().selector } {
f(e, args);
assert e.msg.sender == owner(), "registry mutation requires owner";
}
// CR-RULE-4 FAIL-CLOSED verify (mirrors cryptoregistry_smt.py R1a): a REVOKED id
// never verifies, whatever the routed verifier would answer (the status gate short-
// circuits before the staticcall, so the prover's havoc of the verifier is
// irrelevant).
rule verifyFailClosedOnRevoked(env e, uint256 id, bytes pubKey, bytes32 messageHash, bytes signature) {
require statusOf(id) == AereCryptoRegistry.Status.REVOKED;
bool result = verify(e, id, pubKey, messageHash, signature);
assert result == false, "a REVOKED row must never verify";
}
// CR-RULE-5 FAIL-CLOSED verify for an UNKNOWN id (R1b): a never-registered id
// (id == 0 or id > count) never verifies.
rule verifyFailClosedOnUnknown(env e, uint256 id, bytes pubKey, bytes32 messageHash, bytes signature) {
require id == 0 || id > algorithmCount();
assert verify(e, id, pubKey, messageHash, signature) == false;
}
// CR-RULE-6 resolveActive returns ONLY an ACTIVE id (mirrors R3a): if it returns
// (does not revert), the returned id's status is ACTIVE.
rule resolveActiveReturnsActive(env e, uint256 startId) {
uint256 resolved = resolveActive(e, startId);
assert statusOf(resolved) == AereCryptoRegistry.Status.ACTIVE,
"resolveActive never returns a non-ACTIVE id";
}
```
#### 2.c.2 `AerePQCKeyRegistry` (the PQC key registry)
Source: `aerenew/contracts/contracts/pqc/AerePQCKeyRegistry.sol`. Read confirms:
- Permissionless (no `Ownable`). Storage: `KeyRecord[] _keys` (append-only; keyId is the array index,
pushed in `_registerWithPoP`, never removed), `mapping(address => uint64) identityNonce` (monotonic),
`mapping(address => uint256[]) _identityKeys`.
- Status constants: `STATUS_NONE=0, STATUS_ACTIVE=1, STATUS_ROTATED=2, STATUS_REVOKED=3`. `revokeKey`
is terminal.
- `KeyRecord.owner` is set once (`owner: owner` in the push) and never rewritten. `rotateKey` stores the
successor with `owner == msg.sender == old.owner`.
- `verifyWithKey` is fail-closed: returns false for an unknown key, `STATUS_REVOKED`, or `STATUS_NONE`.
Properties mirror `pqckeyregistry_smt.py` K3 (revoked never verifies), K4 (rotation preserves owner), plus
the append-only statements:
```cvl
// certora/specs/AerePQCKeyRegistry.spec
methods {
function keyCount() external returns (uint256) envfree;
function statusOf(uint256) external returns (uint8) envfree;
function ownerOf(uint256) external returns (address) envfree;
function schemeOf(uint256) external returns (uint8) envfree;
function identityNonce(address) external returns (uint64) envfree;
}
// KR-INV-1 keys are append-only: keyCount is monotonic non-decreasing, so a
// registered keyId is never removed or its slot reused.
rule keyCountMonotonic(method f, env e, calldataarg args) {
mathint before = keyCount();
f(e, args);
assert to_mathint(keyCount()) >= before;
}
// KR-RULE-2 the owner binding of an existing key is immutable: no method rewrites
// an already-registered key's owner (the record's owner field is write-once). This
// is the "recorded entries cannot be silently rewritten" property for identities.
rule keyOwnerImmutable(method f, env e, calldataarg args, uint256 keyId) {
require keyId < keyCount();
address ownerBefore = ownerOf(keyId);
uint8 schemeBefore = schemeOf(keyId);
f(e, args);
assert ownerOf(keyId) == ownerBefore, "a key's owner is write-once";
assert schemeOf(keyId) == schemeBefore, "a key's scheme is write-once";
}
// KR-RULE-3 REVOKED is terminal (K3 precondition): once a key is REVOKED it stays
// REVOKED across every subsequent method.
rule revokeIsTerminal(method f, env e, calldataarg args, uint256 keyId) {
require statusOf(keyId) == 3; // STATUS_REVOKED
f(e, args);
assert statusOf(keyId) == 3, "a REVOKED key can never leave REVOKED";
}
// KR-RULE-4 FAIL-CLOSED verifyWithKey on a REVOKED key (mirrors pqckeyregistry_smt
// K3): a revoked key never verifies, whatever the precompile would answer.
rule verifyWithKeyFailClosedRevoked(env e, uint256 keyId, bytes32 message, bytes signature) {
require statusOf(keyId) == 3; // STATUS_REVOKED
assert verifyWithKey(e, keyId, message, signature) == false;
}
// KR-RULE-5 the per-identity PoP nonce is monotonic (K2 backbone): every
// registration advances identityNonce, and no method decreases it, so a consumed
// proof-of-possession challenge can never recur for the same identity.
rule identityNonceMonotonic(method f, env e, calldataarg args, address who) {
mathint before = to_mathint(identityNonce(who));
f(e, args);
assert to_mathint(identityNonce(who)) >= before;
}
```
The K1 / K2 domain-separation results (the PoP challenge binds owner and nonce so a proof cannot be
replayed across identities or re-used by the same identity) rest on keccak injectivity. In Certora that is
provable by hashing the challenge tuple through the linked `keccak` model and asserting the two challenge
preimages differ; it is written as a `[MEASURE]` extension (`AerePQCKeyRegistry_pop.spec`) because it
needs the hashing summary set up, and it restates `pqckeyregistry_smt.py` K1/K2 at the bytecode layer.
`[MEASURE: run certoraRun certora/conf/registries.conf and publish the AereCryptoRegistry /
AerePQCKeyRegistry rule report]`
---
### 2.d Threshold / precompile callers: the duplicate-committee-key fix
Source: `aerenew/contracts/contracts/mpc/AereThresholdAccount.sol` (ERC-4337 t-of-n PQC account) and the
shared library `aerenew/contracts/contracts/mpc/AerePQCThreshold.sol`. The sibling
`AereThresholdPQCRegistry.registerCommittee(uint8,uint8,bytes[])` carries the identical fix. Read confirms:
- `initialize(address _entryPoint, uint8 _scheme, uint8 _threshold, bytes[] pubKeys)` is factory-only and
one-shot (`AlreadyInitialized` if re-called). It validates `1 <= _threshold <= n <= 255` and, at the
one place a committee is ever set, **rejects duplicate public keys**: it hashes each key and reverts
`DuplicatePubKey(i)` if `keccak256(pubKeys[i]) == keccak256(pubKeys[j])` for any `j < i`.
- Authorization (`validateUserOp`, `executeThreshold`) counts distinct signers by member INDEX via the
`seen` bitmask in `_countMem`, and returns success only when `valid >= threshold`.
- `scheme`, `threshold`, `size`, `membersHash`, `_pubKeys` are immutable after `initialize`.
This is the exact target of `aerenew/formal-consensus/threshold_account_smt.py` (P1: with distinct keys,
no single keyholder can reach threshold `t >= 2`). The CVL splits it into the part provable at bytecode
level and the combinatorial part that stays in z3, keeping the same boundary the z3 file itself declares.
```cvl
// certora/specs/AereThresholdAccount.spec
methods {
function threshold() external returns (uint8) envfree;
function size() external returns (uint8) envfree;
function scheme() external returns (uint8) envfree;
function membersHash() external returns (bytes32) envfree;
function entryPoint() external returns (address) envfree;
function pubKeyAt(uint8) external returns (bytes) envfree;
}
// TH-RULE-1 committee bounds hold after a successful initialize: a real committee
// always has 1 <= threshold <= size <= 255. In particular threshold >= 1, so an
// empty signer set never authorizes, and threshold <= size, so the committee is
// satisfiable only by genuine members.
rule committeeBoundsAfterInit(env e, address ep, uint8 sch, uint8 t, bytes[] pubKeys) {
initialize(e, ep, sch, t, pubKeys);
assert threshold() >= 1, "threshold is at least 1";
assert threshold() <= size(), "threshold never exceeds committee size";
assert to_mathint(size()) == pubKeys.length;
}
// TH-RULE-2 the committee is set ONCE and is immutable thereafter: a second
// initialize always reverts, and no method changes scheme / threshold / size /
// membersHash after the first init. So the distinct-key set fixed at init can never
// be swapped for a duplicate-laden one later.
rule committeeImmutableAfterInit(method f, env e, calldataarg args) {
require entryPoint() != 0; // already initialized
uint8 t0 = threshold();
uint8 n0 = size();
uint8 s0 = scheme();
bytes32 mh0 = membersHash();
f(e, args);
assert threshold() == t0, "threshold immutable post-init";
assert size() == n0, "size immutable post-init";
assert scheme() == s0, "scheme immutable post-init";
assert membersHash() == mh0, "membersHash immutable post-init";
}
// TH-RULE-3 DISTINCT KEYS ENFORCED AT INIT (the 2026-07-15 dup-key fix): if
// initialize succeeds, then no two stored committee members share a public key.
// Expressed over the two-member witness that the pairwise loop rejects: for any
// distinct in-range indices i, j, the stored keys differ. This is the bytecode-level
// statement of "the same keyholder cannot occupy two committee slots".
rule initRejectsDuplicateKeys(env e, address ep, uint8 sch, uint8 t, bytes[] pubKeys,
uint8 i, uint8 j) {
initialize(e, ep, sch, t, pubKeys); // reverts on any duplicate; reaching here => no dup
require i < size() && j < size() && i != j;
assert keccak256(pubKeyAt(i)) != keccak256(pubKeyAt(j)),
"no two committee members share a public key after a successful init";
}
```
Design-level restatement of `threshold_account_smt.py` P1, expressed in CVL over an abstract committee
(the same combinatorial layer, and the same boundary, that the z3 file declares: it models the counting
logic, not the full Solidity `_countMem` loop over an unbounded `Leg[]`):
```cvl
// certora/specs/ThresholdCounting.spec (abstract-committee model)
//
// key(i) = the id of the keyholder controlling slot i. count(h) = number of slots h
// controls. The seen-bitmask counts distinct INDICES, so "valid signers" <= number of
// distinct indices supplied. TH-RULE-3 gives distinct KEYS per index, hence distinct
// keyholders per counted index. This ghost model proves the P1 consequence: with
// pairwise-distinct keys, no single keyholder h controls >= threshold slots for t >= 2.
ghost mapping(uint256 => uint256) key; // slot -> keyholder id
ghost uint256 committeeSize;
// P1: distinct committee keys => every keyholder fills at most one slot, so reaching
// a threshold t >= 2 requires at least t distinct keyholders.
rule distinctKeysImplyThresholdNeedsDistinctHolders(uint256 h, uint256 t) {
require t >= 2;
// committee keys are pairwise distinct (established on-chain by TH-RULE-3):
require forall uint256 a. forall uint256 b.
(a < committeeSize && b < committeeSize && a != b) => key[a] != key[b];
// NEGATION: some single keyholder h controls >= t slots.
mathint slotsForH = 0;
// (in the runnable spec this is a bounded fold over committeeSize; shown here as
// the property the fold asserts)
assert !(exists uint256 s1. exists uint256 s2.
s1 < committeeSize && s2 < committeeSize && s1 != s2
&& key[s1] == h && key[s2] == h),
"with distinct keys no keyholder occupies two slots, so t>=2 needs t distinct holders";
}
```
Honesty note on TH: TH-RULE-1/2/3 are bytecode-level rules against the real `AereThresholdAccount`. The
abstract-committee P1 rule is the CVL transcription of the z3 combinatorial proof and carries the same
"not the compiled counting loop" boundary that `threshold_account_smt.py` states in its own summary. The
z3 negative controls (P2/P3: without the distinct-key check one keyholder reaches threshold alone) remain
in z3; the CVL equivalent is simply TH-RULE-3 failing if the `DuplicatePubKey` guard is removed.
`[MEASURE: run certoraRun certora/conf/threshold.conf and publish the AereThresholdAccount rule report]`
---
## 3. `certora/` layout, run config, and commands
Proposed tree under the contracts package (`aerenew/contracts/`, so `certoraRun` resolves imports through
the existing Hardhat / Foundry remappings):
```
aerenew/contracts/
certora/
specs/
AereSink.spec
AereCoinbaseSplitterV2.spec
AereFeeBurnVault.spec
sAEREv2.spec
AereCryptoRegistry.spec
AerePQCKeyRegistry.spec
AerePQCKeyRegistry_pop.spec # K1/K2 keccak domain-separation (MEASURE extension)
AereThresholdAccount.spec
ThresholdCounting.spec # abstract-committee P1 model
harness/
DummyERC20A.sol # faithful minimal ERC20 for the linked asset
AereSinkHarness.sol # exposes internal getters if needed
conf/
burnstack.conf
sAEREv2.conf
registries.conf
threshold.conf
README.md # this status doc's run section, copied for the repo
```
`harness/DummyERC20A.sol` is the same faithful minimal ERC-20 the Halmos suite already uses
(`aerenew/formal-contracts/specs/AereSink.symbolic.t.sol` MockERC20): exact-amount `transfer`,
`transferFrom`, `approve`, `balanceOf`. Fee-on-transfer and rebasing tokens are out of scope (excluded by
the asset-listing policy), the same bound the Halmos SK-series declares.
Example run config (Certora conf files are JSON):
```json
// certora/conf/burnstack.conf
{
"files": [
"contracts/sink/AereSink.sol",
"contracts/AereCoinbaseSplitterV2.sol",
"contracts/AereFeeBurnVault.sol",
"certora/harness/DummyERC20A.sol"
],
"verify": "AereSink:certora/specs/AereSink.spec",
"link": [ "AereSink:AERE=DummyERC20A" ],
"solc": "solc0.8.23",
"optimistic_loop": true,
"loop_iter": "3",
"rule_sanity": "basic",
"msg": "Aere burn stack: sink 3-bucket immutability + splitter caps + burn-vault no-withdraw"
}
```
```json
// certora/conf/sAEREv2.conf
{
"files": [
"contracts/staking/sAEREv2.sol",
"certora/harness/DummyERC20A.sol"
],
"verify": "sAEREv2:certora/specs/sAEREv2.spec",
"link": [ "sAEREv2:_asset=DummyERC20A" ],
"solc": "solc0.8.23",
"optimistic_loop": true,
"rule_sanity": "basic",
"msg": "sAERE v2: donation resistance + totalAssets consistency + drip monotonicity"
}
```
```json
// certora/conf/registries.conf
{
"files": [
"contracts/pqc/AereCryptoRegistry.sol",
"contracts/pqc/AerePQCKeyRegistry.sol"
],
"verify": "AereCryptoRegistry:certora/specs/AereCryptoRegistry.spec",
"solc": "solc0.8.23",
"optimistic_loop": true,
"rule_sanity": "basic",
"msg": "Aere PQC registries: fail-closed verify + append-only rows + no re-point"
}
```
```json
// certora/conf/threshold.conf
{
"files": [
"contracts/mpc/AereThresholdAccount.sol",
"contracts/mpc/AerePQCThreshold.sol"
],
"verify": "AereThresholdAccount:certora/specs/AereThresholdAccount.spec",
"solc": "solc0.8.23",
"optimistic_loop": true,
"loop_iter": "4",
"rule_sanity": "basic",
"msg": "Aere threshold account: distinct-key committee + set-once immutability"
}
```
Exact commands (each requires the `certora-cli` package and a `CERTORAKEY` from `prover.certora.com`):
```bash
# one-time setup (NOT run in this environment)
pip install certora-cli
export CERTORAKEY=<key from prover.certora.com>
# from aerenew/contracts/
certoraRun certora/conf/burnstack.conf
certoraRun certora/conf/sAEREv2.conf
certoraRun certora/conf/registries.conf
certoraRun certora/conf/threshold.conf
# run a single rule while iterating:
certoraRun certora/conf/burnstack.conf --rule noFundsToArbitraryAddress
# separate registry spec run (AerePQCKeyRegistry has its own verify target):
certoraRun certora/conf/registries.conf --verify AerePQCKeyRegistry:certora/specs/AerePQCKeyRegistry.spec
```
Each `certoraRun` uploads the job to Certora's cloud back end and returns a job URL whose HTML report
lists every rule as `Verified`, `Violated` (with a counterexample), or `Timeout`. That report is the
artifact to publish.
---
## 4. Honest status
### 4.1 What is written here versus what is proved
Everything in Section 2 is **CVL that is authored and matched to the real contract interfaces read from
source in this repository.** The method signatures, state-variable getters, enum members, status
constants, and function selectors are taken from the actual `.sol` files, not from documentation.
**No rule in this document is proved.** The Certora Prover has not been run. The distinction matters:
"authored and interface-matched" is a claim about the CVL text; "proved" is a claim about a solver result,
and we do not have one.
### 4.2 Which targets have real CVL versus flags
| Target | Source file read | CVL status |
|---|---|---|
| `AereSink` | `contracts/sink/AereSink.sol` | Real CVL against real getters (`BURN_BPS`, `BURN_VAULT`, `flush`, `sweepDust`). AERE-branch rules exact; router branch is a marked `[MEASURE]` extension. |
| `AereCoinbaseSplitterV2` | `contracts/AereCoinbaseSplitterV2.sol` | Real CVL (`burnBps`, `sinkBps`, `MAX_BURN_BPS`, `setBps`, `owner`). |
| `AereFeeBurnVault` | `contracts/AereFeeBurnVault.sol` | Real CVL (`totalBurnedAERE`, `sweepToZero`, `nativeBalances`). |
| `sAEREv2` | `contracts/staking/sAEREv2.sol` | Real CVL (`totalAssets`, `undistributedRewards`, `sync`, `convertToAssets`). SR-RULE-4 designed to distinguish v2 from the un-fixed v1. |
| `AereCryptoRegistry` | `contracts/pqc/AereCryptoRegistry.sol` | Real CVL (`getAlgorithm`, `statusOf`, `verify`, `resolveActive`, `Status` enum). |
| `AerePQCKeyRegistry` | `contracts/pqc/AerePQCKeyRegistry.sol` | Real CVL (`keyCount`, `ownerOf`, `statusOf`, `verifyWithKey`, `identityNonce`). K1/K2 keccak domain-separation flagged `[MEASURE]` extension. |
| `AereThresholdAccount` + `AerePQCThreshold` | `contracts/mpc/AereThresholdAccount.sol`, `AerePQCThreshold.sol` | Real CVL for init bounds / set-once / distinct-key (TH-1/2/3). The P1 keyholder-counting theorem is an abstract-ghost model, same boundary as `threshold_account_smt.py`. |
| `AereThresholdPQCRegistry` | `contracts/mpc/AereThresholdPQCRegistry.sol` | Not separately specced here; its `registerCommittee` dup-key guard is byte-identical to `AereThresholdAccount.initialize`, so TH-RULE-3 transfers. Adding a dedicated spec is a `[MEASURE]` follow-up. |
No target required a `[VERIFY: locate <contract>.sol]` flag: every contract named in the task was located
and read. The only interface caveat is the sAERE target selection, which points at `sAEREv2.sol` (the
R7-fixed redeploy) rather than the live `sAERE.sol`; this is stated inline at Section 2.b.
### 4.3 The measurement gate (nothing is proved until these run)
| # | Command | Publishes |
|---|---|---|
| M1 | `certoraRun certora/conf/burnstack.conf` | `[MEASURE]` PASS/FAIL for SK-INV-1/2, SK-RULE-3/4, CS-INV-1/2/3, CS-RULE-4, FB-INV-1, FB-RULE-2/3 |
| M2 | `certoraRun certora/conf/sAEREv2.conf` | `[MEASURE]` PASS/FAIL for SR-RULE-1, SR-INV-2/3, SR-RULE-4 |
| M3 | `certoraRun certora/conf/registries.conf` | `[MEASURE]` PASS/FAIL for CR-INV-1, CR-RULE-2..6, KR-INV-1, KR-RULE-2..5 |
| M4 | `certoraRun certora/conf/threshold.conf` | `[MEASURE]` PASS/FAIL for TH-RULE-1/2/3 and the P1 ghost model |
`[MEASURE: run each conf above with certoraRun and publish the resulting Certora rule report (the job-URL
HTML listing every rule as Verified / Violated / Timeout). Only after a rule shows Verified may it be
described as proved. Until then it is CVL authored, not run.]`
### 4.4 Publication statement
When this document is published, the accurate framing is: "Aere Network has authored Certora CVL
specifications for its most safety-critical contracts (burn stack, sAERE, the crypto and PQC-key
registries, and the post-quantum threshold account), matched to the deployed contract interfaces and
reusing the property statements already checked at the design level in z3. Executing them to a machine
report requires the Certora Prover (free tier at prover.certora.com; `certora-cli`), which produces the
per-rule Verified / Violated result. That run is pending; no rule is claimed proved before it."

View File

@ -0,0 +1,60 @@
# Spec 10: Aere Compute Market V3 (proof-carrying DePIN)
## Delivered
1. `aerenew/contracts/contracts/depin/AereComputeMarketV3.sol` (new; deployed V1/V2 untouched)
2. `aerenew/contracts/test/AereComputeMarketV3.test.js` (13 tests, all passing)
3. `aerenew/docs/AERE-COMPUTE-MARKET-V3.md`
## What it does
Proof-carrying compute marketplace. Requester posts a job (specHash, reward escrow, deadline,
verification mode); provider claims + runs off-chain; result acceptance depends on the mode:
- REPLAY (0): deterministic recompute. Result bytes emitted; challenge window; dispute records the
challenger's recomputed hash; arbiter resolves.
- ZK_VERIFIED (1): SP1 proof verified on-chain via the deployed SP1 gateway (ISP1Verifier). publicValues
bound to the job (abi.encode(specHash, resultHash), specHash must match). Paid in the SAME tx, only on
a valid proof. No other pay path exists for a ZK job.
- OPTIMISTIC (2): challenge window + provider bond. Successful dispute slashes provider bond to the
challenger, refunds requester.
PQC settlement path: any job can set pqcSettlement + an 897-byte Falcon-512 key. Final payout then
requires a Falcon-512 signature (verified via live precompile 0x0AE1) over a digest binding
(chainId, contract, jobId, provider, token, amount, resultHash). Fail-closed. This is payout
authorization only; it does NOT make consensus post-quantum (documented).
Settlement asset: native AERE (address(0)) or an allowlisted ERC-20 (governance-gated allowlist).
Bonds always native AERE. No new token.
## Fail-closed / invariants (all tested)
- Escrow solvency: totalLiabilities[token] = sum(open rewards + posted bonds); contract balance always
>= liabilities per asset. isSolvent() view. Asserted after every mutating step in every test.
- No double-pay: terminal statuses (Paid/Refunded/Slashed) reached once; nonReentrant; CEI ordering.
- No pay-without-proof (ZK): settle inside submitResultZK after verifyProof (reverts on invalid).
- No admin drain: no owner, no sweep. Arbiter only routes a DISPUTED job's escrow; governance only
flips a token-allowlist bool.
- Fee-on-transfer/rebasing reward tokens rejected at escrow (received must equal stated reward).
## Real test result
`npx hardhat test test/AereComputeMarketV3.test.js` (from aerenew/contracts):
13 passing (~2s). Covers: ZK pay-on-valid + reject-invalid + wrong-spec rejection; OPTIMISTIC
finalize + dispute-slash; REPLAY dispute (provider upheld) + off-chain re-run match; ERC-20 reward
allowlist accept/reject; Falcon valid-accept + tampered-reject + wrong-amount-reject +
sig-on-nonpqc-job reject; solvency across a mixed batch; deadline reclaim with bond slash.
## Grounding used
- Built on AereComputeMarketV2 job/escrow model (depin/).
- ZK: ISP1Verifier.sol + SP1VerifierGateway.sol; deployed gateway 0x9ca479C8c52C0EbB4599319a36a5a017BCC70628.
- Falcon: input encoding + fail-closed raw-32-byte return pattern from AerePQCMessageVerifier.sol;
test uses MockPQCPrecompile(1) @0x0AE1 and MockSp1Verifier (reverts on invalid), MockERC20Lending.
## Flags
- [VERIFY] ZK path tested vs MockSp1Verifier (mirrors gateway revert-on-invalid); real gateway wired at deploy.
- [VERIFY] Falcon path tested vs MockPQCPrecompile; real wire encoding KAT-proven elsewhere; 0x0AE1 live on 2800.
- [MEASURE] Real GPU supply, off-chain provers, real SP1 proving are external (adoption, not code).
- Not deployed; founder-gated.

156
AERE-COMPUTE-MARKET-V3.md Normal file
View File

@ -0,0 +1,156 @@
# Aere Compute Market V3
Proof-carrying decentralized compute (DePIN) settlement for Aere Network.
## What it is
`AereComputeMarketV3` is the on-chain verification and settlement rail for a decentralized compute
marketplace. A REQUESTER posts a job (a commitment to the workload spec, a reward held in escrow, a
deadline, and a required verification mode). A PROVIDER claims the job, runs the work off-chain, and
submits a result. The provider is paid only against verifiable evidence, never on trust alone. Which
evidence counts, and when payment releases, is decided by the job's VERIFICATION MODE.
V3 unifies the verification rails that were split across the earlier coordination layer
(`AereComputeMarketV2`, which offered a single optimistic path with Foundation arbitration) and adds a
post-quantum settlement gate. The deployed V1 and V2 DePIN contracts are untouched; V3 is a new,
independent contract.
Source: `aerenew/contracts/contracts/depin/AereComputeMarketV3.sol`
Test: `aerenew/contracts/test/AereComputeMarketV3.test.js` (13 passing)
## The three verification modes
A requester picks one mode per job. Each mode answers one question: what makes a submitted result
payable?
### REPLAY (mode 0)
The result is a deterministic function of the input. On submission the provider posts the full result
bytes, which the contract emits (and anchors as `keccak256(result)`), so any watcher can re-run the
spec locally and compare `keccak256(localResult)` against the on-chain `resultHash`. The result is
accepted after a challenge window unless a challenger posts a matching bond and disputes. On a dispute
the challenger records the `resultHash` they recomputed, so the arbiter (and any observer) can
mechanically verify who is correct. The arbiter then resolves.
When to use it. Workloads that are pure, deterministic, and cheap to re-execute (batch transforms,
deterministic simulations, reproducible builds). Disputes are objectively decidable by re-running, so
the arbiter's role is mechanical rather than judgemental.
### ZK_VERIFIED (mode 1)
The result carries a zero-knowledge proof, verified ON-CHAIN through the existing deployed SP1 gateway
verifier. The provider submits the SP1 public values (ABI-encoded as `(bytes32 specHash, bytes32
resultHash)`) and the proof. The contract binds the proof to the job by requiring the proven `specHash`
to equal the job's committed spec, records the proven `resultHash`, then calls the gateway's
`verifyProof`. The reward releases in the SAME transaction, and ONLY if the proof verifies. There is no
other code path that pays a `ZK_VERIFIED` job, so paying one without a valid proof is impossible.
When to use it. Workloads where correctness must be enforced without trusting a challenge window or an
arbiter: verifiable inference, zk coprocessing, anything where the requester wants finality on delivery
rather than after a delay. The cost is that the provider must run a real prover.
The verifier reference. V3 calls the `ISP1Verifier` interface
(`aerenew/contracts/contracts/zkverify/ISP1Verifier.sol`), the same interface implemented by the
deployed `SP1VerifierGateway` at `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628` on Aere Network chain
2800. V3 does not reimplement proof verification; it references and calls the existing gateway. The
gateway routes on the proof's first four bytes to the correct SP1 verifier and reverts on an invalid
proof, which is exactly the fail-closed behavior V3 relies on.
### OPTIMISTIC (mode 2)
The result is accepted after a challenge window unless a challenger posts a bond and disputes. A
successful dispute slashes the PROVIDER bond to the CHALLENGER and refunds the reward to the requester.
Unlike REPLAY, the result is not assumed cheaply re-derivable, so the provider stakes a bond as skin in
the game and the challenge is a general fraud claim rather than a mechanical recomputation.
When to use it. Workloads that are checkable but not cheaply reproducible (large or non-deterministic
pipelines, results that are expensive to recompute but easy to spot-check). The bond sizes the cost of
lying.
## Post-quantum settlement path
Any job (in any mode) can set `pqcSettlement = true` and register an 897-byte Falcon-512 settlement
key. When set, the final release of funds to the provider additionally requires a Falcon-512 signature
by that key over a domain-separated settlement digest binding `(chainId, contract, jobId, provider,
token, amount, resultHash)`. The signature is verified IN FULL on-chain by the live native precompile
at `0x0AE1` (activated on Aere mainnet 2800 at block 9,189,161), using the same input encoding proven
against the live precompile in `AerePQCMessageVerifier`.
The point: the payout authorization is post-quantum. A quantum adversary who forged the classical
secp256k1 transaction sender still cannot authorize a payout without the Falcon private key. The digest
binds the exact payout figure and result, so an authorization can never be replayed to another job,
chain, contract, or amount. A tampered or missing signature reverts the release and nothing is paid
(fail-closed).
Scope boundary. This gate authorizes PAYOUTS. It does NOT make Aere consensus post-quantum. Blocks
remain Besu QBFT with classical secp256k1 validators. What is post-quantum here is the authenticity of
the settlement authorization, not block production.
Disputed jobs are settled by the arbiter's ruling rather than the happy-path release, so the Falcon
gate applies to the normal accept/finalize/ZK settle paths; arbiter resolution of a disputed job is the
arbiter's authority.
## Escrow-solvency guarantee
The contract tracks `totalLiabilities[token]` for every asset: the sum of open job rewards plus posted
bonds in that asset. Every escrow or bond increments it; every payout, refund, or slash decrements it by
the same amount, with effects applied before transfers. The invariant
```
balanceOf(contract, token) >= totalLiabilities[token] for every asset
```
therefore holds at all times, where `token == address(0)` is native AERE. The public view `isSolvent`
checks it. Rewards settle in native AERE or an allowlisted ERC-20; provider and challenger bonds are
always native AERE, so solvency is tracked per asset. Fee-on-transfer or rebasing reward tokens are
rejected at escrow time (the contract requires the received amount to equal the stated reward).
The test asserts `isSolvent` (and the raw balance-versus-liability comparison) after every mutating step
of every scenario, including a mixed batch of jobs across all three modes carried to their terminal
states.
## Fail-closed properties
- No double-pay. Every job reaches a terminal status (`Paid`, `Refunded`, `Slashed`) exactly once. All
fund-moving entrypoints are reentrancy-guarded and set the terminal status before any transfer.
- No pay-without-proof in ZK_VERIFIED. Settlement happens inside `submitResultZK`, after the gateway
`verifyProof` call. An invalid proof reverts the whole transaction.
- No admin drain. There is no owner and no sweep. The arbiter can only route a DISPUTED job's escrow
between its requester, provider, and challenger. Governance can only flip a token allowlist boolean.
Neither can withdraw escrowed funds.
- Deadlines. A claimed job that misses its deadline without a submission lets the requester reclaim the
reward and slashes the provider bond to the requester.
## Job lifecycle (summary)
```
postJob Open requester escrows reward (+ mode/pqc params)
claimJob Claimed provider posts the required native-AERE bond (0 for ZK)
submitResult Submitted REPLAY / OPTIMISTIC: result bytes emitted, window starts
submitResultZK Paid ZK_VERIFIED: proof verified on-chain, paid in the same tx
acceptResult Paid requester accepts a Submitted result early
finalize Paid anyone, after the window closes with no dispute
dispute Disputed challenger posts a matching bond within the window
resolveDispute Paid/Slashed arbiter rules; a bad result slashes the provider bond to the challenger
cancelJob Refunded requester reclaims an unclaimed job's escrow
reclaimExpired Refunded requester reclaims a claimed job that blew its deadline (bond slashed)
```
## Honest status
- Contract: built and tested. `AereComputeMarketV3.sol` compiles under solc 0.8.23 (the repo standard)
and the 13-case Hardhat suite passes, covering all three modes, the Falcon settlement gate, the ERC-20
reward path, the solvency invariant, and no-double-pay.
- ZK verification path [VERIFY]: exercised against `MockSp1Verifier`, which mirrors the deployed gateway
semantics (reverts on an invalid proof). The reference verifier is the deployed `SP1VerifierGateway`
at `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`; wiring V3 to it on mainnet is a deploy-time
constructor argument.
- Falcon settlement path [VERIFY]: exercised against `MockPQCPrecompile` at `0x0AE1`, which parses the
Falcon-512 spec offsets. The real wire encoding is separately KAT-proven in `AerePQCMessageVerifier`
and against the live mainnet precompile. On mainnet 2800 the precompile at `0x0AE1` is live.
- Real compute supply [MEASURE]: real GPU / accelerator supply, the off-chain provers, and the real SP1
proving are EXTERNAL to this contract. They require actual hardware providers running the reference
provider daemon. This is adoption, not code, exactly like validator decentralization. V3 is the trust
and settlement rail; it does not by itself constitute a live compute market with real providers.
- No new token. Rewards, escrow, bonds, and payment are native AERE or an allowlisted existing ERC-20.
- Not deployed. This contract is built for publication and review. Deployment is founder-gated.

View File

@ -0,0 +1,479 @@
# Aere Network Cross-Client Determinism
Technical specification. Chain 2800 (Aere Network mainnet).
Grounding and consistency: this document is the reproducible proof behind Aere's
client-diversity claim. It is consistent by construction with section 5.5 of
`aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md` (the two clients disagree on state)
and the consensus row of `aerenew/docs/AERE-ARCHITECTURE-MAP.md`. Where those documents
and this one describe the same status, they say the same thing. If they ever diverge,
the engineering spec 5.5 is authoritative and this document is wrong.
Evidence cited here lives in `aerenew/nethermind-pqc/` (the second-client work and its
proof logs), `aerenew/conformance/` (the executable conformance vector suite),
`aerenew/docs/NETHERMIND-2ND-CLIENT-LIVE-2026-07-17.md`,
`aerenew/docs/DECENTRALIZATION-2ND-CLIENT-RUNBOOK.md`, and
`aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md`.
---
## 1. Why cross-client determinism matters, and the honest scope
### 1.1 A single producing client is a systemic risk
A blockchain is a deterministic state machine: for one ordered sequence of inputs (the
blocks) there is exactly one correct output (the state). A network that runs a single
client implementation has no independent check on whether that implementation computes
the one correct output. If the sole client has a consensus-relevant bug, the whole
network inherits it, and there is nothing to compare against that would reveal the error
before it is finalized.
The reference case is the September 2025 Reth halt. A chain whose block production
depended on one implementation stopped when that implementation hit a bug. The failure
was not in the protocol; it was in the monoculture. This is the exact risk Aere's
second-client program exists to retire, and it is the reason `AERE-ENGINEERING-SECURITY-SPEC.md`
section 4.2 records "a single producing client is a demonstrated systemic risk" as the
design driver.
Aere's live mainnet today runs one production client: a fork of Hyperledger Besu v26.4.0
(base commit d2032017, JDK 21), the sole producer for all seven QBFT validators. A second,
independent implementation that computes the same state from the same inputs turns "trust
the one client" into "two independent implementations agree", which is a materially
different and stronger assurance.
### 1.2 What this proves, and what it does not
The second client is a patched Nethermind 1.39.0 (.NET), chosen for implementation
diversity: a different codebase in a different language, so a consensus-relevant bug in
one is unlikely to exist in the other. A second Besu instance would not be client
diversity, because it would share every Besu bug.
This document proves one specific property:
> **State-computation determinism across two independent implementations.** For the same
> chain-2800 inputs, the patched Besu producer and the patched Nethermind client compute
> byte-identical block hashes and byte-identical state roots, and agree on seal validity
> and post-quantum precompile outputs.
This document does **not** claim any of the following, and the reader should not infer them:
- It does **not** claim Nethermind is a live producing validator on mainnet. It is not.
Nethermind runs as a non-validating follower and as a cross-check; it takes zero part in
live consensus. Besu remains the sole live producer.
- It does **not** claim client-diverse consensus or a "two-client mainnet". Making
Nethermind a live sealer is a separate, founder-gated, high-risk change documented in
`DECENTRALIZATION-2ND-CLIENT-RUNBOOK.md` Phase B, not started and not authorized here.
- It does **not** claim post-quantum consensus. Consensus is classical secp256k1 ECDSA
QBFT. The post-quantum surface is signatures, accounts, and applications only. This scope
boundary holds throughout every section below.
The value delivered is real and bounded: two independent implementations demonstrably
compute the same state, so a consensus-relevant divergence between them is detectable
rather than silent. The gap between that assurance and a fully automated live fail-safe
gate is stated plainly in section 4.
---
## 2. The determinism protocol
This is the method a third party uses to verify cross-client determinism directly. It has
five parts, each an independent check on the same underlying property. Each part states
what is asserted and what evidence supports it. Reproduction commands are in section 5.
### 2.a Full re-validation to genesis (zero invalid seals)
**Assertion.** The patched Nethermind re-validates chain-2800 blocks from genesis forward
through its own header-import and seal-validation code path, accepting every genuine header
and asserting zero invalid seals, and rejecting any tampered header.
**Method.** Nethermind reproduces the genesis exactly
(`0xd86d57a899cbfa580669f0e1c7cd4ae5f525247c3c823f8f75df176a0c5d7f1a`), then for each block
recomputes the QBFT canonical block hash (extraData re-encoded seal-stripped, round kept),
recovers each 65-byte committed seal to a distinct in-set validator address, and requires
the recovered-signer count to meet the QBFT quorum `ceil(2N/3)` (N=7, quorum 5). This runs
inside Nethermind's production `HeaderValidator` / `ISealValidator` seam, subclassed as
`AereQbftHeaderValidator` and `AereQbftSealValidator`, so it is the client's own validation
logic, not a bespoke script.
**Evidence.**
- `aerenew/nethermind-pqc/QBFT-VALIDATION-2026-07-12.md` and
`aerenew/nethermind-pqc/QBFT-FOLLOWER-2026-07-12.md`: real chain-2800 headers driven
through Nethermind's own `HeaderValidator.Validate()`; genuine headers accepted, four
tamper modes rejected. Vectors in `aerenew/nethermind-pqc/test/AerePqc.Tests/qbft-vectors.json`
(16 real chain-2800 headers).
- Live follower re-validation from genesis forward with zero invalid-seal rejects over the
synced range (section 2.c).
**Honest boundary.** Header and seal re-validation is proven over sampled real headers and
over the contiguous ranges the follower has synced from genesis. A single unbroken
re-validation across the entire history to current head, asserting zero invalid seals over
all of it, completes when the permanent follower finishes its sync to head.
[VERIFY: at-head, full-history re-validation with a zero-invalid-seal count over the complete
0..head range is pending the permanent follower reaching head; per
`NETHERMIND-2ND-CLIENT-LIVE-2026-07-17.md` section 6 this is an explicit open TODO.]
### 2.b Byte-identical re-execution (block hash AND state root at each height)
**Assertion.** Replaying real chain-2800 blocks through both clients yields an identical
block hash **and** an identical state root at each height. State-root identity is the strong
check: it means both implementations executed every transaction, applied every reward and
fee rule, and arrived at the same world state, not merely copied a header.
**Method.** Full-sync mode (`FastSync/SnapSync/FastBlocks = false`) so every block is
executed and its post-state root is recomputed by the node, then compared against the
reference client's state root at the same height.
**Evidence.**
- `aerenew/nethermind-pqc/QBFT-GENESIS-SYNC-FIX-2026-07-13.md`: a real Nethermind node
full-syncs from genesis, re-executes all transactions, and every node-computed state root
matches the real chain exactly. Genesis stateRoot
`0x2d2255d41483c22e8a2d51ca18249157e25a47f949e8a6c54552d0f790b7b127`. Spot-checked
read-only against the live chain:
| height | node-computed stateRoot | live chain-2800 stateRoot | match |
|---|---|---|---|
| 458 | `0xc314d2c3…` | `0xc314d2c3…` | yes |
| 694 | `0xf0a4309e…` | `0xf0a4309e…` | yes |
| 872 | `0xcda2107d…` | `0xcda2107d…` | yes |
| 1661 | `0x44baac17…` | `0x44baac17…` | yes |
| 3000 | `0x44baac178e561fe2…243e8575` | `0x44baac178e561fe2…243e8575` | yes |
This run is bounded to the first 3000 blocks (22 transactions re-executed), genuine
full-state re-derivation over that range, not a header copy.
- `aerenew/docs/NETHERMIND-2ND-CLIENT-LIVE-2026-07-17.md` section 4: the live permanent
follower matches `rpc.aere.network` on block hash AND state root at heights 1, 100, 1,000,
10,000, 11,133, and 11,136 (head at check time).
**Honest boundary.** Byte-identical re-execution is proven over the ranges executed to date
(offline genesis..3000, and the live follower's synced heights). The cross-boundary check at
the AerePQC precompile fork (block 9,189,161) and at current head completes when the
permanent follower crosses that height.
[MEASURE: state-root identity at the fork boundary 9,189,160 and 9,189,161 and at current
head, per `DECENTRALIZATION-2ND-CLIENT-RUNBOOK.md` A.6, is verified once the follower is at
head. A mismatch at or across the boundary would indicate a wrong precompile gate.]
### 2.c Live follower cross-check
**Assertion.** A listen-only Nethermind following the live chain over devp2p matches the
live RPC's block hash and state root at each checked height, with zero invalid-seal rejects.
**Method.** The follower is hardened to be non-validating: no validator key, no `AERE_*`
environment, `eth/68`-only on the wire (the `istanbul/100` QBFT capability is stripped), so
the engine, producer, and importer paths are inert. It peers to nodes under Foundation
control (rpc2 primary, infra secondary), full-syncs from genesis, and its per-height
hash and state root are compared to `rpc.aere.network`.
**Evidence.**
- `aerenew/docs/NETHERMIND-2ND-CLIENT-LIVE-2026-07-17.md`: the permanent second client
(a dedicated host, not publicly reachable, run as a durable service). Parity table of hash and
state root MATCH at 1 / 100 / 1,000 / 10,000 / 11,133 / 11,136; zero invalid-seal rejects
in range.
- The first live-devp2p run (recorded in the memory note `client_diversity_2026-07-13` and
the live doc) full-executed genesis to block 182,777 and matched hash and state root at
1 / 100 / 1,000 / 10,000 / 100,000 / 111,130 / 182,777, with zero divergence in range.
**Honest boundary.** The follower is a listen-only cross-check, not a validator. As of the
recorded state it is syncing toward head (honest ETA roughly 18 hours from the recorded
start; DB projected near 48 GB at head).
[VERIFY: "at head, tracking within a few blocks of `rpc.aere.network`, zero divergence over a
sustained soak" per runbook A.6/A.7 is the completion criterion and is pending the follower
reaching and holding head.]
### 2.d Generated edge-case corpus
**Assertion.** Adversarial and edge-case inputs, run through both clients, widen coverage
beyond organic blocks. Organic chain-2800 blocks are near-empty, so replaying only real
blocks under-exercises the state-transition surface. A generated corpus of adversarial
transactions and malformed inputs closes that gap.
**Status, split by layer:**
- **Precompile edge-case corpus: EXISTS.** `aerenew/conformance/conformance-vectors.json`
carries the adversarial precompile surface directly: valid, wrong-key, tampered-signature,
tampered-nonce or message, wrong-header-byte, wrong-length, empty, too-short,
below-minimum, and at-minimum-all-zero cases for each verifier, plus the full vendored NIST
ACVP negative test groups (ML-DSA-sigVer-FIPS204 tg8: 15 records, 3 pass / 12 fail;
SLH-DSA-sigVer-FIPS205 tg31: 14 records, 2 pass / 12 fail) and the SHAKE256 output-length
domain including the over-cap truncation edge. These are the "trap for a second client"
cases and they are pinned as vectors (`aerenew/conformance/README.md`).
- **Block-level and transaction-level adversarial corpus: [MEASURE], to build.** A dedicated
corpus of adversarial or edge-case transactions (deep call stacks, storage-collision
patterns, gas-edge and revert-edge transactions, EIP-2935 lookback boundary calls, base-fee
floor boundary blocks) executed through both clients with state-root diff at each step does
not yet exist as a committed artifact. It is the corpus to build to widen coverage beyond
organic blocks. [MEASURE: generate an adversarial block/transaction corpus, replay through
both clients, assert identical state root per step; store under `aerenew/conformance/` next
to the precompile vectors.]
### 2.e PQC precompile parity
**Assertion.** Both clients produce identical outputs for the same NIST vectors through the
0x0AE1..0x0AE5 precompiles (Falcon-512, Falcon-1024, ML-DSA-44, SLH-DSA-SHA2-128s, SHAKE256).
**Method.** The five Aere post-quantum precompiles are compiled into a real Nethermind
1.39.0 process via its own `IPrecompileProvider` extension seam (the same interface Optimism
and Taiko use), gated to activate at the fork boundary. NIST KAT and conformance vectors are
driven through the running Nethermind JSON-RPC by `eth_call` and compared byte-for-byte
against the Besu fork's output for the same input.
**Evidence.**
- `aerenew/nethermind-pqc/REAL-NETHERMIND-PQC-2026-07-12.md`: 27 of 27 NIST-KAT and
conformance vectors pass byte-for-byte via `eth_call` inside a genuine running Nethermind
1.39.0 (commit `14aca2c5`), matching the Besu 26.4.0 fork. Includes the tampered-signature
negative for each of Falcon-512, Falcon-1024, ML-DSA-44, and SLH-DSA-128s (each returns the
32-byte zero word, not-verified).
- `aerenew/conformance/`: the executable conformance suite has since grown to 83 precompile
vectors (Falcon-512 x12, Falcon-1024 x12, ML-DSA-44 x22, SLH-DSA-SHA2-128s x21, SHAKE256
x16), 112 vectors total including QBFT, EIP-2935, and extraData. Last live run: 112/112
passed against the chain-2800 Besu reference (`aerenew/conformance/README.md`). Note the
distinction: the 27/27 figure is the set re-run byte-for-byte inside the actual Nethermind
process; the 112/112 figure is the full suite confirmed against the live Besu reference by
`runner.mjs`.
**Precompile fail-closed semantics (both clients must match).** On malformed input the
signature precompiles (0x0AE1..0x0AE4) return a 32-byte zero word (not-verified) and never
revert; SHAKE256 (0x0AE5) returns empty when input is under 32 bytes or `outLen` is zero, and
truncates an over-cap `outLen` to 65536 rather than reverting. A second client that reverts,
or returns empty where the reference returns a zero word, diverges. These semantics are
observed-live, not assumed, and are pinned as vectors.
**Gate parity (a real divergence trap).** The precompiles must activate at exactly the Aere
fork boundary, block 9,189,161 (the first block with timestamp >= 1783820272), gated by
`chainId == 2800`. An ungated build (active from block 0) would diverge state if any pre-fork
block ever called an `0x0AE*` address as an ordinary account. The permanent follower gates on
the futureEips timestamp in `ReleaseSpec.cs` (`IsAerePqcEnabled`) and
`ChainSpecBasedSpecProvider.CreateReleaseSpec`, verified inactive pre-fork by an `eth_call`
to `0x0AE5` returning `0x` on the pre-fork head versus the SHAKE output on the post-fork head
(`NETHERMIND-2ND-CLIENT-LIVE-2026-07-17.md` section 3).
---
## 3. Existing evidence (byte-identical production and import)
Beyond the follower and precompile parity above, the second client has been shown to produce
QBFT blocks that the reference client accepts byte-for-byte, and to have that acceptance be
non-vacuous (a tampered seal is rejected). This is the production-encoding half of client
diversity. All of it is on an isolated test network; the live chain, the live validators, and
the infra box were never touched.
### 3.1 Byte-for-byte block production, then Besu import
`aerenew/nethermind-pqc/QBFT-PRODUCER-2ND-CLIENT-2026-07-13.md` (with proof log
`proofs/qbft-producer-besu-import-proof-2026-07-13.txt` and
`proofs/qbft-producer-manifest-2026-07-13.json`):
- **Byte-exact calibration.** Given the same key, genesis, and timestamp, the Nethermind-side
producer's full block RLP is byte-for-byte identical to Besu's `debug_getRawBlock(1)`: 1286
hex characters equal, including the ECDSA committed seal and the block hash
`0xa34f5e43…f6c616`.
- **Production interop.** The producer sealed 8 empty QBFT blocks; a fresh stock Besu 24.12.2
node imported all 8 through its real BFT block-validation path (offline `blocks import`,
which runs the same `BftCommitSealsValidationRule` and `MainnetBlockValidator` as live
import). Every imported block hash equals the producer's manifest; miner is the Nethermind
validator address.
### 3.2 Non-vacuity: a one-byte-tampered seal is rejected
Same proof, control C: one byte of block 1's committed seal was flipped
(`0x8d -> 0x72`), leaving the block hash unchanged so the check isolates the seal. Genuine
block imported; tampered block rejected by Besu's own rule
(`BftCommitSealsValidationRule | Invalid block header: Not all committers are in the locally
maintained validator list`, `tampered exit: 1`). The 8-block acceptance is therefore genuine
cryptographic agreement, not a rubber stamp.
### 3.3 Cross-client hash agreement across proposers
`aerenew/nethermind-pqc/proofs/mixed-client/proposer/01-onchain-alternation-hashmatch.txt`: on
a mixed set, proposers strictly alternate Besu and Nethermind, and the on-chain block hash
computed by the Nethermind node equals the Besu node's hash at every one of 20 consecutive
blocks (NM_hash == Besu_hash, YES, blocks 1..20). At N=4 (2 Besu + 2 Nethermind), all four
nodes agree on the head hash (`proofs/mixed-client/follower-sync/REGRESSION-n4.txt`, 4-way head
hash agreement at block `0x2d`).
### 3.4 PQC precompile parity inside a real Nethermind
Section 2.e: 27/27 NIST KAT and conformance vectors byte-for-byte via `eth_call` inside a
running Nethermind, matching the Besu fork
(`aerenew/nethermind-pqc/REAL-NETHERMIND-PQC-2026-07-12.md`); the broader 112/112 conformance
suite against the live Besu reference (`aerenew/conformance/`).
---
## 4. Honest status
This is the load-bearing honesty section. It states, without rounding up, exactly what is
proven and what is remaining operational work. It matches section 5.5 of
`AERE-ENGINEERING-SECURITY-SPEC.md`; the two must stay consistent.
**PROVEN, as a validation cross-check.** The second client demonstrably re-derives state
independently and would detect a state divergence. Concretely, it re-validates chain-2800
headers and seals through its own validation code (accept genuine, reject tampered);
re-executes real blocks to byte-for-byte identical block hashes and state roots (offline over
genesis..3000, and as a live listen-only follower matching the live RPC's hash and state root
at every checked height in range); and produces identical outputs for the five PQC precompiles
on the same NIST vectors. Because there is exactly one definition of correct state and two
independent implementations computing it, a consensus-relevant divergence (a differing state
root, a differing block hash, a differing seal decision) between Besu and Nethermind is
detectable by comparing the two. It does not silently pass. This is real client-diversity
assurance.
**NOT yet a live automated divergence gate.** The cross-check is not yet wired into production
monitoring as an automated, alerting fail-safe that watches Besu and Nethermind continuously
and raises an alarm on the first byte of divergence. Today the comparison is run and
spot-checked; it is not a continuously-armed live gate.
**NOT yet a live producing validator.** Nethermind is a non-validating follower and a
cross-check. It takes zero part in live consensus. Besu is the sole live producer on chain
2800. A Nethermind disagreement flags a discrepancy against the producer; it does not arbitrate
consensus, because Nethermind casts no vote and seals no block on mainnet.
**Behavior on a detected divergence.** This is a fail-safe, not an automatic consensus action.
A detected divergence is a signal to halt and investigate before the divergent block is
trusted, not something that itself forks or halts the live chain. In all adversarial fault
testing to date, zero forks were observed; the chain's failure posture is to halt and later
recover, not to fork.
**The remaining operational work** is turning the proven cross-check into an automated live
fail-safe gate: a supervised process that continuously reads both clients at head, diffs block
hash and state root per block, and alerts (and gates trust) on the first divergence, with the
permanent follower held at head under a sustained soak. Separately and further out, making
Nethermind a live sealing validator is the founder-gated, high-risk Phase B in
`DECENTRALIZATION-2ND-CLIENT-RUNBOOK.md` (extended mixed-client soak at production shape,
independent audit of the Nethermind QBFT engine, a rehearsed one-block rollback vote, and
fault tolerance preserved by construction), not started and not authorized by this document.
Public-copy rule that travels with this status: a second independent client follows and
validates live chain 2800 as a non-validating full node. Do not write "client-diverse
consensus", "two-client mainnet", or anything implying the second client seals live blocks.
The Nakamoto coefficient by operator is still about 1; a follower does not change it. Never
claim mainnet consensus is post-quantum; it is classical secp256k1 ECDSA.
---
## 5. Reproduction
A third party reproduces the differential by building both clients, replaying blocks, and
diffing state roots. Steps that need the fork host or a heavy full-history build are marked
[MEASURE] with the command rather than an asserted fresh number, because the numbers above
were measured on Foundation infrastructure and the point of reproduction is that an independent
party gets the same bytes, not that they trust ours.
### 5.1 Precompile parity and conformance suite (light, no chain build)
The executable spec runs read-only against the live reference and needs only `ethers` v6:
```bash
cd aerenew/conformance
node runner.mjs # validates every vector against the live Besu reference (read-only)
node build-vectors.mjs # regenerates vectors from NIST KATs + real chain-2800 headers
```
Expected: all vectors pass; `runner.mjs` re-confirms the live N=7 validator set and the
`validator_set_root` before asserting the QBFT vectors. This reproduces the precompile-output
half of determinism (section 2.e) against the reference without building a client.
To reproduce the same 27/27 inside a second client rather than against the reference, build
the patched Nethermind (5.2) and drive the vectors through its `eth_call`, per
`aerenew/nethermind-pqc/REAL-NETHERMIND-PQC-2026-07-12.md`.
### 5.2 Build the two clients
- **Reference (Besu fork).** Hyperledger Besu v26.4.0, base commit d2032017, JDK 21, plus the
Aere PQC precompile and EIP-2935 fork rules. [MEASURE: build on a scratch box, not infra;
Aere's live producer is not rebuilt for this. See `ops_no_heavy_compute_on_infra`.]
- **Second client (patched Nethermind).** Nethermind 1.39.0 commit `14aca2c5`, .NET SDK
10.0.30x, with the in-tree `Nethermind.AerePqc` project applied:
```bash
cd aerenew/nethermind-pqc/nethermind-intree
./apply.sh && ./apply2.sh && ./apply3.sh # precompiles + release-spec gate
# for the header/seal follower add through apply9.sh; for the producer path add apply5.sh
dotnet build src/Nethermind/Nethermind.Runner -c Release
```
For the non-validating follower build, exclude the istanbul-dependent QBFT engine and the
producer so the binary is `eth/68`-only, and leave `AERE_PRODUCER_KEY` and `AERE_QBFT_KEY`
unset (this is the invariant that keeps it non-validating). Gate the precompiles on the
futureEips timestamp so they activate at block 9,189,161, not block 0.
### 5.3 Full re-validation to genesis (2.a)
Start the follower in full-sync from genesis against a node under your control and confirm
zero invalid-seal rejects in the logs:
```bash
# config: chainspec with the reproduced genesis 0xd86d57a8…0c5d7f1a,
# --Sync FullSync (FastSync/SnapSync/FastBlocks = false), static peer to a Besu node,
# discovery off, RPC bound to 127.0.0.1
dotnet Nethermind.Runner.dll --config aere-live-config.json
```
[MEASURE: full-history re-validation to head with a zero-invalid-seal count over 0..head
requires the multi-hour full-history sync on the fork host; assert the count once the follower
is at head.]
### 5.4 Byte-identical re-execution, diff the state roots (2.b)
For each checked height, pull the state root from both clients and diff. Read-only, via each
client's own RPC (the follower on `127.0.0.1:8545`, the reference on its node RPC):
```bash
for h in 1 100 1000 10000 3000; do
a=$(cast rpc eth_getBlockByNumber $(printf '0x%x' $h) false --rpc-url http://127.0.0.1:8545 | jq -r .stateRoot)
b=$(cast rpc eth_getBlockByNumber $(printf '0x%x' $h) false --rpc-url <reference-node-rpc> | jq -r .stateRoot)
[ "$a" = "$b" ] && echo "$h stateRoot MATCH" || echo "$h stateRoot DIVERGE a=$a b=$b"
done
```
Do the same for `.hash` (block hash) at each height. Expected: MATCH at every height, per the
tables in section 2.b.
[MEASURE: extend the height list across the fork boundary (9,189,160 and 9,189,161) and to
current head once the follower has executed past 9,189,161. A mismatch at or across the
boundary indicates a wrong precompile gate; stop and fix.]
**baseFee reproduction trap.** Compare state roots and block hashes against a raw Besu node,
not the public `rpc.aere.network`. The public RPC is behind a proxy that overwrites
`baseFeePerGas` with a constant 1 Gwei for display; that shimmed value does not reproduce the
real block hash for blocks whose true base fee differs. A client reading directly from a Besu
node gets the true consensus value and needs no override (`aerenew/conformance/README.md`,
baseFee shim caveat).
### 5.5 Production interop and tamper control (3.1, 3.2)
On an isolated testnet, have the Nethermind-side producer seal a short chain and import it with
a stock Besu, then repeat with one seal byte flipped:
```bash
# produce N blocks -> nm-blocks.rlp (isolated chainId, single validator), then:
besu --data-path <fresh> blocks import --from nm-blocks.rlp # expect exit 0, all hashes match the manifest
# flip one byte of a committed seal, re-import -> expect BftCommitSealsValidationRule rejection, exit 1
```
Expected: genuine chain imports with byte-identical hashes; the tampered chain is rejected by
Besu's own BFT rule. This is the non-vacuity control, reproduced from
`aerenew/nethermind-pqc/QBFT-PRODUCER-2ND-CLIENT-2026-07-13.md`.
### 5.6 Live follower cross-check (2.c)
Run the non-validating follower against the live chain and diff hash and state root against a
raw live node at a spread of heights:
[MEASURE: the live follower cross-check at head requires the follower to be fully synced (honest
ETA roughly 18 hours) and peered to a live node; assert per-height hash and state-root MATCH and
zero divergence over a sustained soak per `DECENTRALIZATION-2ND-CLIENT-RUNBOOK.md` A.6 and A.7.]
---
## 6. Summary
Two independent implementations, in different languages, compute the same chain-2800 state:
identical block hashes, identical state roots, matching seal decisions, and byte-for-byte
identical post-quantum precompile outputs, with a tampered seal provably rejected. That is
proven as a validation cross-check. It is not yet a continuously-armed automated live
divergence gate, and Nethermind is not yet a live producing validator; those are the remaining
operational and founder-gated steps. The claim this document supports is precise and defensible:
Aere's client-diversity assurance is a demonstrated, reproducible cross-check on state
computation, not a marketing line, and its boundaries are stated in the same words as the
engineering spec it is consistent with.

View File

@ -0,0 +1,55 @@
# Summary: AERE-NEW-CONTRACTS-DEPLOY-RUNBOOK.md
Deliverable written to `aerenew/docs/AERE-NEW-CONTRACTS-DEPLOY-RUNBOOK.md`. Covers 17 new founder-gated
contracts, read directly from source under `aerenew/contracts/contracts/`. Nothing is deployed; every one
is founder-gated (confirmed: none has a `deployments/*.json` entry, none in `sdk-js/src/addresses.ts`).
## Method
- Read every contract's real constructor.
- Cross-referenced live addresses from `sdk-js/src/addresses.ts` and precompiles from
`docs/AERE-EIP-COMPATIBILITY-MATRIX.md` (0x0AE1 Falcon-512, 0x0AE3 ML-DSA-44, both live block 9,189,161).
- Mapped per-contract risk from `docs/AERE-SECURITY-REVIEW-NEW-CONTRACTS.md` + `AERE-SECURITY-FIXES.md`.
- Named the Hardhat test file for each contract.
## Live wiring addresses used
- SP1VerifierGateway 0x9ca479C8c52C0EbB4599319a36a5a017BCC70628
- AerePQCKeyRegistry 0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691
- AereAgentDID 0xce641d7d7C10553D82b06B7C21d423550e7522C5
- AereAIReputation 0x781ef746c08760aa854cDa4621d54db6734bfeBF
- AerePQCAccountFactory 0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58 (runtime ref for migrator)
- AERE402FacilitatorV2 0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de
- Foundation 0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3
- Deployer 0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465
## Deploy order (topological)
Wave A (no sibling dep): AereAccountMigrator, AereRandomnessBeaconV2, AereRecoveryRegistry,
AereDestinationSettler, AerePQAttestationKeyRegistry, AereBitstringStatusList(0), AereIdentityRegistry8004(DID),
AereValidationRegistry8004(reg,DID), AereAP2MandateVerifier(reg,executor), AereTrustRegistry(reg),
AereVectorStore(facilitator), AereComputeMarketV3(gateway,arbiter,governance).
Wave B: AereReputationRegistry8004(reputation, identity=A7), AereVerifiableCredential(trust=A10, statusList=A6).
Wave C (circuit-gated): AerePQAggregateVerifier(gateway, aggVkey), AereFinalityCertificateVerifier(gateway, finVkey, registry=A5).
Wave D: AerePQAggregateModule(verifier=C1).
## Gate buckets
- Deploy-when-ready (low risk): Migrator, RandomnessBeaconV2, RecoveryRegistry, DestinationSettler,
IdentityRegistry8004, ValidationRegistry8004, VerifiableCredential, BitstringStatusList, VectorStore.
- Founder-config/action before useful: AP2 (executor), TrustRegistry (owner+accredit),
ReputationRegistry8004 (attestor grant + owner), AttestationKeyRegistry (enroll+keys), ComputeMarketV3
(arbiter+governance).
- External-audit-gated (fund flow, strongest = ComputeMarketV3): also recommended for VectorStore,
DestinationSettler, AccountMigrator.
- MUST NOT deploy until a circuit exists: AerePQAggregateVerifier, AereFinalityCertificateVerifier,
AerePQAggregateModule, and the zk non-revocation path of AereBitstringStatusList.
## [VERIFY] items at deploy
- AP2 settlementExecutor (non-zero, required in prod; L3).
- ReputationRegistry8004 reputation instance for write-through (new instance listing the adapter).
- VectorStore facilitator (AERE402FacilitatorV2; confirm settle ABI).
- ComputeMarketV3 arbiter + governance (Foundation recommended).
- vkeys (immutable, circuit not built): AerePQAggregateVerifier.aggregateProgramVKey,
AereFinalityCertificateVerifier.programVKey, AereBitstringStatusList.setNonRevocationVKey.
## Security-review carry-through
No CRITICAL/HIGH. M1 (reputation write gate) fixed, L1 x3 (code-less-gateway guard) fixed in
AerePQAggregateVerifier/AereComputeMarketV3/AereBitstringStatusList, L2 (vector-store receipt-provider bind)
fixed, L3 (AP2 open-path replay) documented as accepted risk closed by the executor gate.

View File

@ -0,0 +1,126 @@
# Aere Network: Ethereum EIP Compatibility Matrix
**Chain:** Aere Network mainnet, chain ID 2800 (`0xaf0`). Execution client: Hyperledger Besu 26.4.0 with QBFT consensus (classical secp256k1 ECDSA, N=7 validators, f=2), approximately 0.5 second blocks. This document is a developer reference: it states, EIP by EIP, where Aere's EVM matches Ethereum mainnet and where it deliberately differs.
Aere tracks Ethereum's hard forks so that its EVM surface matches mainnet at the same addresses and opcodes: London and Shanghai apply from genesis, Cancun and Prague (Pectra) activated together at block 2,075,363, and Osaka (Fusaka) at block 2,106,606. A small set of deviations is intentional and disclosed here, because Aere is a QBFT chain with no separate beacon (consensus) layer and no blob data availability market. Anything not matching Ethereum is a design decision, not an oversight, and each is called out in the matrix and again in the deviations recap at the end.
## How to read this
- **Status** is one of: **Supported** (matches Ethereum), **Partial** (present but materially limited on Aere), **Not implemented** (absent), **No-op** (present in the ruleset but neutralized, returns a defined zero/empty value under QBFT).
- Every row is intended to be literally true. Cells that could not be verified against the repository's genesis or fork configuration carry an explicit **[VERIFY: ...]** note for a human to confirm before publication.
- "The fork" or "the AerePQC hard fork" refers to Aere's client-only Besu `futureEips` milestone (`futureEipsTime = 1783820272`), which activated at block 9,189,161 (2026-07-12) and bundled the native post-quantum precompiles, the EIP-2935 write path, and EIP-7939. No re-genesis was involved.
### Fork timeline on chain 2800
| Ethereum fork | Aere activation | Notes |
|---|---|---|
| London, Shanghai | Genesis (block 0) | `londonBlock: 0`, `shanghaiTime: 0` in genesis config |
| Cancun + Prague (Pectra) | Block 2,075,363 | `cancunTime == pragueTime == 1780189051`; Cancun and Prague share one activation instant on Aere |
| Osaka (Fusaka) | Block 2,106,606 | `osakaTime == 1780220351` |
| AerePQC milestone (post-Osaka) | Block 9,189,161 | Besu `futureEipsTime == 1783820272`; PQC precompiles + EIP-2935 write path + EIP-7939 |
[VERIFY: block heights 2,075,363 and 2,106,606. The genesis config gates Cancun/Prague/Osaka by timestamp (1780189051 / 1780189051 / 1780220351), not by block number, so these heights are the observed first-block-past-timestamp and should be confirmed against the live chain's head history.]
---
## The Matrix
### London and Shanghai era (active from genesis)
| EIP | Name | Status | Difference from Ethereum |
|---|---|---|---|
| 1559 | Fee market (base fee, type-2 txs) | Partial | Fee-market mechanics present, plus a 1 Gwei base-fee floor live since block 10,141,734 (raised the real minimum from 7 wei). Aere adds no consensus base-fee burn narrative: its "burn" is a separate validator-reward cut (see deviations). [VERIFY: base-fee disposal on chain 2800, whether the 1559 base fee is protocol-burned as on Ethereum or credited.] |
| 3198 | BASEFEE opcode | Supported | Returns the current base fee, which is floored at 1 Gwei (see EIP-1559). |
| 3529 | Reduction in refunds | Supported | Matches Ethereum. |
| 3541 | Reject contract code starting with 0xEF | Supported | Matches Ethereum. Reserves the 0xEF lead byte (the EOF prefix). |
| 3651 | Warm COINBASE | Supported | Matches Ethereum. |
| 3855 | PUSH0 instruction | Supported | Matches Ethereum. |
| 3860 | Limit and meter initcode | Supported | Matches Ethereum. |
| 4895 | Beacon chain push withdrawals as operations | No-op | Withdrawal-operation ruleset present, but Aere has no beacon layer to originate withdrawals, so none are produced. |
### Cancun (block 2,075,363)
| EIP | Name | Status | Difference from Ethereum |
|---|---|---|---|
| 1153 | Transient storage (TLOAD / TSTORE) | Supported | Matches Ethereum. |
| 4844 | Shard blob transactions | Partial | Type-3 blob transactions are present in the EVM, but Aere runs no blob data-availability market, so blobs carry no fee and are not served/persisted as on Ethereum. See EIP-7516. |
| 4788 | Beacon block root in the EVM | No-op | The beacon-root ring buffer returns 0x0: a QBFT chain has no separate beacon/consensus layer to expose a root from. |
| 5656 | MCOPY instruction | Supported | Matches Ethereum. |
| 6780 | SELFDESTRUCT only in same transaction | Supported | Matches Ethereum. |
| 7516 | BLOBBASEFEE opcode | No-op | Returns 0: there is no blob market, so there is no blob base fee. |
### Prague / Pectra (block 2,075,363)
| EIP | Name | Status | Difference from Ethereum |
|---|---|---|---|
| 2537 | BLS12-381 curve precompiles | Supported | Matches Ethereum. |
| 2935 | Save historical block hashes in state | Supported | Standard 8191-block history-storage ring buffer at `0x0000F90827F1C53a10cb7A02335B175320002935`, written by consensus. Nominally listed at Pectra but the system contract was not populated on mainnet until the AerePQC fork (block 9,189,161) added the write path; from that block `eth_getCode` returns the canonical runtime and the 8191-block window is live. |
| 6110 | Supply validator deposits on chain | Partial | The deposit-request ruleset is part of the Pectra parity Aere tracks, but Aere has no beacon layer to consume deposits, so a deposit does not create or fund a QBFT validator (the validator set is QBFT-managed). [VERIFY: whether the deposit system contract is actually populated on mainnet 2800; EIP-2935's contract was not populated until block 9,189,161, so this must be checked directly.] |
| 7002 | Execution-layer triggerable withdrawals/exits | Partial | Withdrawal-request ruleset present, but no beacon layer consumes exits, so it does not affect the QBFT validator set. [VERIFY: system contract populated on mainnet; header requests handling.] |
| 7251 | Increase MAX_EFFECTIVE_BALANCE (consolidations) | Partial | Consolidation-request ruleset present, but no beacon layer to act on it under QBFT. [VERIFY: system contract populated on mainnet.] |
| 7549 | Move committee index outside signature | Not implemented | Beacon consensus-layer change only, with no execution-layer/EVM surface. It has no applicability under QBFT. |
| 7623 | Increase calldata cost | Supported | Matches Ethereum (calldata floor-cost accounting). |
| 7685 | General purpose execution layer requests | Partial | The requests framework (the umbrella for 6110/7002/7251) exists in the ruleset, but Aere has no beacon layer to consume the requests. [VERIFY: whether mainnet block headers carry a non-empty requests hash and the request system contracts are populated.] |
| 7702 | Set EOA account code (delegation) | Supported | Matches Ethereum. This is the EOA-delegation primitive Aere carries (and the reason EIP-3074 is not needed). |
| 7691 | Blob throughput increase | Partial | The blob schedule is raised at Prague (target 6, max 9) in config, but with no blob data-availability market the increase has no practical effect. |
### Fusaka / Osaka (block 2,106,606) and the post-Osaka milestone
| EIP | Name | Status | Difference from Ethereum |
|---|---|---|---|
| 7825 | Transaction gas limit cap (2^24 = 16,777,216) | Supported | Matches Ethereum; the per-transaction gas cap is enforced on chain 2800 (real transactions have executed under it). |
| 7939 | Count leading zeros (CLZ) opcode | Supported | Present. On Aere it activated at the post-Osaka AerePQC milestone (block 9,189,161), which the live EIP-2124 fork-id folds in. [VERIFY: activation height for 7939 specifically, confirmed against the live fork schedule.] |
| 7594 | PeerDAS (peer data availability sampling) | Not implemented | No active role under QBFT: Aere runs no blob data-availability market, so there is no data-availability sampling role to perform. |
| 7805 | FOCIL (fork-choice enforced inclusion lists) | Not implemented | No-op under QBFT: there is no fork-choice or proposer-inclusion layer to enforce inclusion lists against. |
| 7692 | EOF (EVM Object Format) mega-set | Not implemented | Not activated under the current ruleset; the 0xEF prefix stays reserved by EIP-3541 (legacy behavior). [VERIFY: confirm the AerePQC `futureEips` protocol spec (futureEipsTime = 1783820272, block 9,189,161) does NOT enable the EOF opcode set or EIP-3540/3670 container validation. Corroboration that EOF is off: the served genesis config and the Nethermind live-sync fork-id mirror both carry no EOF transition, and the engineering spec states EOF is not activated. The one residual risk is that this milestone rides Besu's `futureEips` key, which in stock Besu is EOF's staging ground, so the fork build's spec definition should be inspected to confirm EOF was not pulled in.] |
### Superseded
| EIP | Name | Status | Difference from Ethereum |
|---|---|---|---|
| 3074 | AUTH / AUTHCALL (sponsored EOA actions) | Not implemented | Deliberately absent. It was superseded by EIP-7702, which Aere carries as its EOA-delegation primitive. |
---
## Aere additions beyond Ethereum
These are callable addresses that are not part of the base Ethereum mainnet precompile set. The post-quantum band `0x0AE1`..`0x0AE5` is genuinely Aere-specific and live on mainnet; `0x0AE6`..`0x0AE8` are not on mainnet; the secp256r1 precompile at `0x100` is a shared RIP that Ethereum is standardizing in Osaka (listed here because it sits outside the classic precompile set).
| Address | Name | Status | Notes |
|---|---|---|---|
| `0x0AE1` | Falcon-512 signature verify | Live on mainnet | Activated at block 9,189,161 (AerePQC fork). Thin wrapper over Bouncy Castle 1.83; NIST KAT verified. |
| `0x0AE2` | Falcon-1024 signature verify | Live on mainnet | Activated at block 9,189,161. NIST KAT verified. |
| `0x0AE3` | ML-DSA-44 signature verify (FIPS 204) | Live on mainnet | Activated at block 9,189,161. ACVP verified. |
| `0x0AE4` | SLH-DSA-SHA2-128s signature verify (FIPS 205) | Live on mainnet | Activated at block 9,189,161. ACVP verified. |
| `0x0AE5` | SHAKE256 (FIPS 202) | Live on mainnet | Activated at block 9,189,161. |
| `0x0AE6` | ML-KEM-768 encapsulation (FIPS 203) | Testnet only | Built and ACVP-verified (25/25) on an isolated QBFT testnet. NOT on mainnet 2800; a mainnet staticcall hits an empty account and returns empty. Founder- and audit-gated. |
| `0x0AE7` | Falcon HashToPoint (SHAKE256 sampler) | Testnet only | Built and KAT-verified (12/12) on isolated testnet. NOT on mainnet 2800. Founder- and audit-gated. |
| `0x0AE8` | BabyBear/FRI STARK verify (skeleton) | Reference skeleton only | Non-working, fail-closed by construction (returns empty for every input). Not deployed on mainnet and explicitly must not be activated until the crypto core is ported. Scope caveat (2026-07-19 finding): the generic components confirmed so far verify BabyBear + FRI STARKs (Aere's OWN Plonky3 circuits), NOT SP1 6.1.0, which is Hypercube (KoalaBear multilinear); retargeting to SP1 6.1.0 is a separate ~22 to 32 person-week effort. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`. |
| `0x100` | P256Verify / secp256r1 (RIP-7212 / RIP-7951) | Live on mainnet | Native P-256 signature verification for passkeys/WebAuthn at a fixed cost of about 3,450 gas. Active from the Osaka/Fusaka ruleset. Note: this is a shared Rollup Improvement Proposal being standardized into Ethereum's Osaka, so it is standards-aligned rather than Aere-proprietary. |
All post-quantum precompiles are signature/hash/KEM verifiers on the EVM path. None of them make Aere's consensus post-quantum: mainnet 2800 still seals blocks with classical secp256k1 QBFT.
---
## Deliberate deviations recap
The intentional differences from Ethereum, all a consequence of Aere being a QBFT chain with no beacon layer and no blob market:
1. **Base fee floor plus a validator-reward burn, not a consensus base-fee burn.** Aere applies a 1 Gwei EIP-1559 base-fee floor (live since block 10,141,734). Separately, Aere's "burn" is a cut of the validator coinbase block reward (37.5% under the current configuration, hard-capped at 50%), routed through the `AereCoinbaseSplitterV2` splitter into immutable destinations. This must not be described as a base-fee burn.
2. **EIP-4844 blobs present, EIP-7516 BLOBBASEFEE returns 0.** Blob transactions exist in the EVM, but there is no blob data-availability market, so the blob base fee is 0.
3. **EIP-4788 parent beacon block root returns 0x0.** A QBFT chain has no separate beacon/consensus layer to source a root from.
4. **EIP-3074 not implemented, superseded by EIP-7702.** EIP-7702 is the EOA-delegation primitive Aere carries.
5. **PeerDAS (EIP-7594), FOCIL (EIP-7805), and EOF (EIP-7692) have no active role under QBFT.** PeerDAS has no data-availability role (no blob market), FOCIL has no fork-choice/proposer-inclusion layer to enforce, and EOF is not activated under the current ruleset (0xEF stays reserved by EIP-3541).
6. **Validator-lifecycle requests (EIP-6110/7002/7251/7685) have no beacon consumer.** The requests ruleset is tracked for Pectra parity, but Aere's validator set is QBFT-managed, so deposits/withdrawals/consolidations do not drive it.
7. **EIP-2935 became functionally live at the AerePQC fork (block 9,189,161), not at Pectra.** The Pectra ruleset nominally listed it, but the history-storage contract was not populated on mainnet until the write path shipped in the AerePQC fork.
---
## Open [VERIFY] flags for the human before external publication
1. **Pectra/Fusaka block heights (2,075,363 / 2,106,606).** Genesis gates these by timestamp, not block number. Confirm the heights against the live chain's head history.
2. **EOF off under `futureEips`.** Confirm the AerePQC `futureEips` protocol spec (futureEipsTime = 1783820272, block 9,189,161) does not enable the EOF opcode set / EIP-3540/3670 container validation. Served genesis, the Nethermind fork-id mirror, and the engineering spec all indicate EOF is off; inspect the fork build's spec definition to close the loop, since the milestone rides Besu's `futureEips` key (EOF's usual staging ground in stock Besu).
3. **EIP-1559 base-fee disposal.** Confirm whether the 1559 base fee is protocol-burned (as on Ethereum) or credited on chain 2800. The documented Aere "burn" is a separate validator-reward cut, so the base-fee disposal itself should be stated precisely.
4. **Validator-lifecycle request contracts (6110/7002/7251/7685).** Verify whether the deposit/withdrawal/consolidation system contracts are actually populated on mainnet (EIP-2935's contract was not populated until block 9,189,161) and whether mainnet block headers carry a requests hash. Regardless of the outcome, no beacon layer consumes these requests, so the "Partial" status and the no-effect-on-validator-set caveat hold.
5. **EIP-7939 activation height.** Confirm CLZ activated at the post-Osaka AerePQC milestone (block 9,189,161) rather than at Osaka (block 2,106,606); the Nethermind live-sync mirror maps it to the post-Osaka boundary.
6. **P256Verify framing.** Confirmed at `0x100`, about 3,450 gas, from the Osaka/Fusaka ruleset (RIP-7212 / RIP-7951). Noted as standards-aligned rather than Aere-proprietary; keep that framing if publishing under an "additions" heading.

View File

@ -0,0 +1,313 @@
# Aere Network Engineering and Security Specification
**Status:** Internal draft for adversarial review and for the external audit that follows. Every claim is meant to be checkable against the repository, the live chain (chain ID 2800), or the cited formal-verification artifacts. This document is internally verified and adversarially self-reviewed; it is not yet externally audited. Read every guarantee as "internally verified, not externally attested" until an independent audit completes.
A single framing statement governs the entire document and is repeated because it is the most common thing to get wrong about Aere: smart contracts and accounts on Aere can verify post-quantum signatures (Falcon, ML-DSA, SLH-DSA) through live EVM precompiles; consensus remains classical ECDSA / QBFT, and the on-chain zero-knowledge verifiers remain classical BN254 pairing checks. Aere is post-quantum at the signature and precompile layer and classical at the consensus and ZK verification layers. The chain as a whole is not post-quantum, and this document never claims it is.
---
## Engineering Principles
Aere Network is built on a small set of engineering principles. This chapter is not marketing: each principle is stated with the evidence that backs it, and where a principle is an aspiration not yet fully earned, that is said plainly. A principle without evidence is a slogan, and the point of this document is to avoid slogans.
### Principles we hold and can demonstrate today
- **Fail closed.** A component that cannot do its job safely refuses rather than guesses. The post-quantum precompiles return a not-verified result (a 32-byte zero word, or empty for the hash function) on malformed or invalid input; they never return a false accept and never revert consensus. (See Section 1.2 and Section 5.)
- **No admin on the economic core.** The value-bearing core (AereSink, the burn vault, sAERE) is ownerless and immutable: a repository-wide sweep of the deployed set finds no owner-withdraw, no upgrade proxy, no selfdestruct, no delegatecall. This is the honest, checkable form of the claim. It is deliberately not a blanket "no admin backdoors anywhere": operational contracts (the NAV oracle, compliance screening, the currently-inert governance) have explicit, documented Foundation roles, and hiding that would be the opposite of this principle. (See Section 1.6 and Section 1.7.)
- **Least privilege.** Every Foundation role that does exist is bounded and documented: the NAV oracle can only propose, dismiss, and attest, and cannot alter an attested snapshot; a program registry can disable a malicious program but cannot invalidate already-recorded proofs. (See Section 1.7.)
- **Determinism over cleverness.** Every precompile is a pure function of its input bytes, reading no chain state and no nondeterministic source, so all nodes compute the identical result and gas charge. Across all adversarial fault testing to date, zero forks were observed: the chain halts and recovers rather than forking. (See Assumption A-10 and Section 5.)
- **Formal verification preferred.** Safety-critical properties are machine-checked, not argued in prose: z3 SMT models of consensus locking and of the escrow and threshold-custody invariants (125 proved properties plus 49 counterexample negative controls at last count), Halmos and Solidity SMTChecker symbolic checking, and 22 invariant-property fuzz suites over the burn, economics, and custody contracts. (See Assumption A-11.)
- **Backward compatibility first.** The EVM, Solidity, bytecode, and JSON-RPC are unchanged; differentiation is added at standard extension points (precompiles at reserved addresses, client-only forks). EIP-7702 is the in-place bridge for existing accounts rather than a compatibility-breaking redesign. (See Section 4.)
- **Security over performance.** The choices that trade performance for safety are deliberate: deterministic QBFT finality over a higher-throughput probabilistic protocol, and keeping the burn out of the consensus-critical path so a policy change cannot halt the chain.
### Principles we are still earning (stated honestly, not yet claimed as achieved)
- **Open, reproducible benchmarks.** We are moving toward publishing every benchmark, test, and vector so any third party can reproduce them. This is in progress, not done. Until the repositories and harness are public and the measured coverage number is published, we do not claim it as achieved.
- **Reproducible builds.** A verifiable source-to-SHA256 build of the client and contracts is a roadmap item, not a present guarantee.
- **Independent audit.** Every guarantee in this document is internally verified and adversarially self-reviewed, but not yet externally audited. This document is the bridge to that audit.
---
## 1. Threat Model per Subsystem
Each subsystem below lists what an attacker could realistically attempt and what actually defends against it, including the cases where the defense is a disclosed limitation rather than a cryptographic or economic guarantee.
### 1.1 Consensus (QBFT)
Aere runs Hyperledger Besu (Java, 26.4.0 line) with QBFT, a partially-synchronous BFT protocol. Blocks target roughly 0.5 s. Validator identity is by ECDSA public-key recovery (`ecrecover`) over secp256k1: each committed seal is a 65-byte recoverable signature and the signer address is recovered from it, so there is no stored validator public key at verification time. The live validator set is N = 7, and at N = 7 the QBFT quorum is `ceil(2N/3) = 5` and the Byzantine fault bound is `f = 2`.
Attacker goals and defenses:
- **Forge a block or seal without a validator key.** Defended by ECDSA seal recovery plus the requirement that every recovered committer be a member of the stored validator set, with a per-block quorum of 5 distinct committers. Forgery reduces to forging secp256k1 ECDSA, which is classically hard. It is not quantum-hard: a cryptographically-relevant quantum computer running Shor breaks the seal signature, which is the central reason consensus is described honestly as classical, not post-quantum.
- **Equivocate or split the chain (safety attack).** QBFT tolerates up to `f = 2` Byzantine validators at N = 7 with safety unconditional (no fork) under the standard BFT bound. Beyond 2 Byzantine validators (more than one third), safety is lost: a 3-of-7 collusion can violate agreement. This is a hard bound, not a soft target.
- **Stall the chain (liveness attack) at the fault boundary.** With exactly 2 validators down, all 5 survivors must prepare and commit with zero slack. QBFT round-robin proposer selection plus a doubling round-change timeout means an unlucky proposer sequence can exceed a fixed measurement window before an alive proposer's round succeeds. Adversarial soak testing on an isolated testnet (100 kill-2-of-7 cycles: 40 mixed-client RT=4, 40 mixed-client RT=1, 20 pure-Besu) recorded 0 forks and 100/100 self-resync, with a rare (about 5 percent), temporary, self-healing zero-progress window at the exact f = 2 boundary. That tail is inherent to partially-synchronous BFT (FLP: liveness is eventual, not bounded-time, at the boundary) and was not removable by timeout tuning. It is not a fork and not a permanent halt.
- **Grind proposer selection or exploit locking.** IBFT 2.0 locking (retain the prepared block and prepare-certificate, carry it in round-change, re-propose the justified block) is the property that prevents a stale or conflicting proposal from being committed across a round change. It is formally modeled (`formal-consensus/qbft_locking_smt.py`) and enforced by the live Besu producer.
- **The centralization threat itself.** All 7 validators are Foundation-operated. The Nakamoto coefficient is therefore approximately 1: a single operator (the Foundation) can in principle halt or, if it chose to run more than 2 equivocating nodes, break safety. This is the single most important limitation in this document. It is disclosed, not hidden, and is not mitigated by cryptography; the only present mitigation is operator honesty plus a public decentralization roadmap. See Assumption A-3.
- **Second-client cross-check.** A patched Nethermind validates live chain 2800, runs the PQC precompiles, and produces byte-identical QBFT blocks that stock Besu accepts (demonstrated offline via block import, not yet as a live devp2p producer). It currently acts as a client-diversity fail-safe cross-check. It is not a live producing validator, and until the IBFT locking findings are implemented in its engine and soak-verified against an active-Byzantine scenario, it is safe only as an observing or voting participant below the `q - f` threshold, not as a producer.
### 1.2 Post-Quantum Precompiles
Five signature and hash precompiles are live on mainnet (activated at block 9,189,161) at the address band `0x0AE1`..`0x0AE5`: Falcon-512 (`0x0AE1`), Falcon-1024 (`0x0AE2`), ML-DSA-44 (`0x0AE3`), SLH-DSA-SHA2-128s (`0x0AE4`), and SHAKE256 (`0x0AE5`). Two further precompiles, ML-KEM-768 (`0x0AE6`) and Falcon HashToPoint (`0x0AE7`), are built and KAT-verified on an isolated testnet only and are not activated on mainnet (activation is founder-gated). A STARK-verify precompile design at `0x0AE8` is a reference skeleton, not a working verifier, and is not activated anywhere. All implementations use Bouncy Castle (`bcprov-jdk18on-1.83.jar`) on the client classpath. Measured marginal verify cost: Falcon-512 40,000 gas, Falcon-1024 75,000 gas, ML-DSA-44 55,000 gas, SLH-DSA-SHA2-128s 350,000 gas, SHAKE256 60 gas base plus 12 gas per word.
Attacker goals and defenses:
- **Crash a node or split consensus with malformed input.** A precompile that panicked or behaved non-deterministically on adversarial bytes would be a consensus fault, since every node must compute the identical result and gas charge. The behavior is fail-closed and was verified against the live chain: the signature verifier precompiles (`0x0AE1` through `0x0AE4`) return a 32-byte zero word (read as "not verified") on any signature that does not verify or on malformed input, and SHAKE256 (`0x0AE5`) returns empty on malformed framing; none of them revert. The two non-signature precompiles are additionally source-verified fail-closed: `MLKEM768PrecompiledContract` and `HashToPointPrecompiledContract` catch every `Throwable` and return `Bytes.EMPTY`. (An earlier AIP design draft framed malformed input as "must revert and consume all gas"; that framing was reconciled to the actual as-shipped fail-closed behavior.)
- **Forge a post-quantum signature.** Defended by the NIST-standardized schemes themselves (Falcon, FIPS 204 ML-DSA, FIPS 205 SLH-DSA) as implemented by Bouncy Castle, validated bit-for-bit against official NIST KAT / ACVP vectors committed to the repository, in both accept and crafted-invalid directions. Soundness rests on the NIST PQC hardness assumptions and on the correctness of the Bouncy Castle implementation (Assumptions A-6, A-7).
- **Substitute a weaker curve or downgrade.** The precompile at a given address implements one fixed scheme and parameter set; there is no in-band algorithm negotiation, so there is no downgrade surface at the precompile itself. Algorithm agility is a property of the calling contract, not the precompile.
- **Exhaust gas or denial of service.** Verify cost is fixed per scheme and bounded well below the EIP-7825 per-transaction cap (2^24 gas), so a single verify cannot be inflated by input shape. Falcon-1024 and ML-DSA-44 in pure Solidity exceed the cap and were therefore view-only before the native precompiles; the precompiles are what bring them under the cap for on-chain recording.
- **Confuse "verifies a PQ signature" with "PQ chain".** The threat here is representational, not technical: a reader could over-read the precompiles as post-quantum consensus. The defense is the framing statement at the top of this document, repeated in the guarantees table below.
### 1.3 Zero-Knowledge Verification
Aere hosts a multi-prover on-chain verification surface behind a route-by-selector gateway: SP1 (Groth16 and Plonk), RISC Zero (Groth16), a raw KZG / EIP-4844 path, and a generated Halo2 verifier, with a permissionless attestation registry on top. The application verifiers (storage-proof coprocessor, zk-compliance, zkML, rollup validity, recursive aggregator) hold the SP1 gateway address rather than a concrete verifier, so a new prover version is a single upstream route change.
Attacker goals and defenses:
- **Submit a false proof (soundness break).** Defended by the underlying proof systems (SP1 / Plonky3, RISC Zero, Halo2) being sound, and by the verifiers binding the exact program: an SP1 proof is checked against a `programVKey` that binds a specific guest ELF, a RISC Zero receipt against an `imageId`, and the recursive aggregator binds the inner KoalaBear (SP1Field) verifying-key digest that the recursion verifier actually checked, rather than only the on-chain BN254 vkey. Soundness is an assumption about the proof systems (Assumption A-5), not something this chain proves from first principles.
- **Break the ZK verifier with a quantum computer.** The on-chain verifiers are BN254 Groth16 pairing checks. BN254 pairings are Shor-breakable, so the ZK verification layer is classical and is not post-quantum. This is disclosed. The planned mitigation is a direct hash-based STARK verifier that rests on hash collision-resistance rather than pairings. Scope caveat (2026-07-19 research finding): the skeleton built so far is a real, conformance-confirmed BabyBear + FRI STARK verifier that targets Aere's OWN Plonky3 circuits, and it does NOT verify SP1 6.1.0 proofs. The pinned SP1 6.1.0 is a Hypercube release (KoalaBear multilinear: BaseFold plus sumcheck-zerocheck plus LogUp-GKR, not BabyBear FRI), so removing the SP1 Groth16 wrap is a separate founder-gated retarget to the SP1 Hypercube stack (see `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`), not a near-term step. Either way that verifier is presently a reference skeleton that fail-closes on every input and is not activated. Until it is real, retargeted, audited, and activated, the ZK layer's post-quantum posture is a design, not a property.
- **Poison a shared root or side tree.** The storage-proof coprocessor walks the real Merkle-Patricia state trie from `stateRoot` to the claimed value, hashing each node against its parent reference, with no Foundation-seeded side tree. Anchor-poisoning and inner-vkey-enforcement findings from the 2026-07-17 review are fixed with regression tests in the repository.
- **Register a malicious program.** Program registration is permissionless; the Foundation can disable a malicious program but cannot disable a legitimate one and cannot retroactively invalidate already-recorded proofs. This is a bounded, disclosed Foundation role, not an arbitrary override.
### 1.4 Bridge and Cross-Chain
The cross-chain surface is optimistic intent settlement (AereSpokePool, ERC-7683 / Across-V3 style), an institutional settlement hub (AereSettlementHubV2), Hyperlane-style warp routes, and zk light clients (QBFT and Ethereum). The escrow accounting of the two core contracts is machine-checked: the SpokePool `balance >= totalLocked + totalBond` invariant and the SettlementHub `balance >= committedLiabilities` invariant are each proved inductive in z3 (`formal-consensus/spokepool_smt.py`, `settlementhub_smt.py`), with negative controls that reproduce a real under-backing counterexample when a guard is removed.
Attacker goals and defenses:
- **Drain escrowed principal.** Defended by the proven solvency invariants above: every settle path pays only an allowlisted solver, cancel refunds only the depositor, withdraw returns only the caller's own free bond, and any sweep of residual is strictly bounded to `balance - committed` so it can never touch a parked intent or a posted bond. No path sends funds to an arbitrary caller address.
- **Claim without filling (optimistic-bond attack).** This is the disclosed weak point. A claim does not prove the destination fill; settle releases the user's locked input to the solver after a challenge window unless challenged, and Phase-1 challenge only emits an event (the Foundation arbitrates and slashes off-chain). A rogue or key-compromised allowlisted solver could claim across many orders in one window and lose only the fixed bond, which does not scale with the value at risk. In Phase 1 this is bounded by a permissioned, Foundation-vetted solver allowlist and is documented in the contract NatSpec. It is a trust assumption, not a hidden bug. Before any permissionless phase, the bond must exceed the maximum stealable value per window.
- **Break accounting with a hostile token.** The SpokePool credits nominal input amounts, so a fee-on-transfer or rebasing input token can break its per-token backing invariant (a disclosed low-to-medium finding); the SettlementHubV2 closes this with an explicit asset allowlist that can exclude such tokens. Exposure is nil while inputs are restricted to vetted standard ERC-20s.
- **Forge a cross-chain message or light-client header.** The zk light clients verify proofs of the source chain's consensus rather than trusting a relayer. Their soundness inherits the ZK-layer assumption (Assumption A-5) and, for the QBFT light client, the same classical-BN254 caveat as Section 1.3.
### 1.5 Accounts and Account Abstraction
Aere supports ERC-4337 v0.7 (AereEntryPointV2), EIP-7702 upgrade-in-place, WebAuthn / P-256 passkey smart accounts, a Falcon-512-owned post-quantum smart account (AerePQCAccount), a hybrid ECDSA-plus-Falcon authorizer (AereHybridAuth), and a t-of-n post-quantum threshold account. The chain exposes the standard `ecrecover` (3,000 gas) and a P-256 verification precompile (RIP-7212 / RIP-7951 at `0x100`, 3,450 gas), both at their standard protocol gas costs.
Attacker goals and defenses:
- **Forge account authorization.** Defended by the account's chosen validator: secp256k1 ECDSA, WebAuthn P-256 (device secure-element key that never leaves the device), Falcon-512 (post-quantum), or a hybrid that requires both an ECDSA and a Falcon signature over the same hash so that neither a broken curve nor a broken lattice alone suffices. Passkey security additionally depends on the device secure element and the WebAuthn ceremony (Assumption A-8).
- **Reach threshold with duplicate committee keys.** A 2026-07-15 finding showed the threshold custody and PQC registry accepted duplicate committee keys (distinctness was by index, not by key), so one keyholder could fill multiple slots. The fix is machine-checked (`threshold_account_smt.py`: with distinct keys no single keyholder reaches threshold t >= 2 for N up to 21) and is deployed on-chain as the PQC registry V2; the account-factory V2 redeploy is founder-gated, and both live buggy contracts are empty (0 committees, 0 accounts created), so exposure is nil either way.
- **Exploit the nonce or paymaster surface.** AereEntryPointV2 uses a strictly sequential single-dimension nonce; accounts that switch validators route on a signature prefix byte rather than a 2D nonce key, a named seam rather than a hidden one. The V2 token and stake-quota paymasters are hardened redeploys that fixed confirmed V1 drain and consent bugs, with the V1 contracts deprecated in place.
- **Quantum forgery of a classical account key.** An ECDSA-only or P-256-only account is not post-quantum; only accounts that use the Falcon or hybrid validators are. This is a per-account property that users opt into, not a chain-wide guarantee.
### 1.6 Economic Core
The economic core is immutable and ownerless. AereSink is a 3-bucket router (BURN, BUYBACK, YIELD) with, per its source, no owner, no admin, no governance role, no `setBucket` / `setRecipient` / `setRouter` / `pause`, immutable bucket recipients and immutable basis-point splits that must sum to exactly 10000. sAERE is an ERC-4626 vault. The burn is a cut of the validator block reward, capped by the immutable `MAX_BURN_BPS = 5000` (50 percent) in AereCoinbaseSplitterV2; it is not a base-fee burn. AereFeeBurnVault has no withdraw function and no admin escape hatch. AERE is the native coin; its total supply is fixed at genesis (2.8B) and there is no protocol mint path.
Attacker goals and defenses:
- **Rug: mint to self, admin-withdraw, upgrade-swap, selfdestruct, or delegatecall.** A repository-wide primitive sweep across the deployed contract set found 0 `selfdestruct`, 0 `delegatecall`, 0 upgradeable proxies (no UUPS / transparent / `_authorizeUpgrade` / `upgradeTo`), and 0 owner escape hatch (`adminWithdraw` / `rescueTokens` / `sweepTo`). The concrete meaning of "no one can rug the economic core" is that these specific primitives are absent from the deployed set. This is the honest, checkable form of the claim, not a blanket "no backdoors anywhere" (see Section 1.7).
- **Steal routed fees.** Every AereSink routing path terminates at the no-withdraw burn vault or the sAERE vault; no path sends funds to an arbitrary or caller-supplied address, so there is no theft vector. A failed buyback swap degrades to sweepable dust rather than a lock, and flush and sweep are reentrancy-guarded.
- **Inflate the burn beyond policy.** The burn rate is Foundation-settable but hard-capped at 50 percent by an immutable constant; it cannot be pushed higher, and it cuts the validator reward, not the base fee.
- **Manipulate the sAERE exchange rate (first-depositor / donation inflation).** The vault does not mint shares on a raw transfer, so a direct donation lifts the rate rather than being stealable, and the yield bucket transfer pings the vault's sync so value drips linearly instead of as a sandwichable step. These properties are covered by invariant-property fuzz suites over the burn / economics contracts.
### 1.7 Oracles and Compliance
Operational contracts, in deliberate contrast to the economic core, do have explicit and documented Foundation roles. This is disclosed and is not a hidden backdoor. The correct statement is: there is no admin on the economic core (ownerless, immutable, verifiable on-chain), and the operational contracts have explicit, documented Foundation roles.
Attacker goals and defenses:
- **Falsify a reserve or NAV attestation.** AereNavOracle lets the Foundation (owner) propose, dismiss challenges, and attest a Merkle-rooted reserve snapshot; it explicitly cannot retroactively alter an already-attested snapshot. A proposed snapshot starts a 24-hour challenge window, anyone may challenge, and attestation after a clean window is permissionless. The Foundation's power is bounded to propose / dismiss / attest, and the challenge window plus append-only attestation is the defense against a single bad snapshot; it is a trusted-attestor model, not a decentralized oracle.
- **Abuse compliance or sanctions screening.** Compliance uses SP1 zk-compliance proofs (a user proves off-chain sanctions / residency / jurisdiction screening inside the zkVM, anchoring only a boolean-style clearance, with no PII on chain) and a Privacy-Pools-style compliant-withdrawal pool. The Foundation role here is screening policy, disclosed, not fund custody.
- **Capture governance.** Governor plus Timelock exist but are currently inert (they own nothing and control nothing until an audit-gated transfer). The threat of a captured governance is therefore not live today; it becomes relevant only if and when control is transferred, at which point the Timelock delay is the defense.
- **Oracle staleness or decimals trap.** A known activation-time hazard: AereSink's oracle-floor math assumes 18-decimal tokens, so enabling the oracle against a 6-decimal pair would give false sandwich protection. The oracle is off today (`ORACLE = 0x0` on-chain), and activation is documented as requiring the decimals fix first. No exposure at present.
---
## 2. Guaranteed vs Not-Guaranteed
This table is the centerpiece of the specification. The left column is what Aere will stand behind; the right column is what Aere explicitly does not promise. A guarantee holds only under the assumptions in Section 3.
| Component | What we GUARANTEE | What we DO NOT guarantee |
|---|---|---|
| **Consensus (QBFT)** | Safety (no fork / no equivocation) with up to 2 of 7 validators Byzantine; self-recovery and resync after transient faults (0 forks and 100/100 resync across 100 adversarial kill-2 cycles on testnet); IBFT 2.0 locking modeled and enforced by the live producer. | Safety beyond 2 Byzantine validators (a 3-of-7 collusion can fork); bounded-time liveness at the exact f = 2 boundary (FLP: liveness is eventual, not instantaneous); decentralization (all 7 validators are Foundation-operated, Nakamoto coefficient about 1); post-quantum consensus (seals are classical ECDSA). |
| **PQC precompiles** | Live on mainnet at `0x0AE1`..`0x0AE5`; deterministic, gas-metered verification of Falcon-512/1024, ML-DSA-44, SLH-DSA-SHA2-128s, SHAKE256, validated bit-for-bit against NIST KAT/ACVP vectors; fail-closed (return a not-verified zero word or empty, never a fault). | That any signature scheme is unbreakable (soundness rests on NIST PQC hardness and on Bouncy Castle correctness); that `0x0AE6`/`0x0AE7` (ML-KEM, HashToPoint) or `0x0AE8` (STARK) are live on mainnet (they are not); that verifying PQ signatures makes the chain post-quantum. |
| **ZK verification** | A multi-prover on-chain verification surface (SP1 Groth16/Plonk, RISC Zero, KZG, Halo2) that binds the exact program (vkey / imageId), with a permissionless attestation registry and a real Merkle-Patricia storage-proof coprocessor. | Soundness of the underlying proof systems from first principles (assumed, not proven here); post-quantum security of the on-chain verifiers (BN254 pairings are Shor-breakable); that the planned STARK verifier is live (it is a non-activated reference skeleton). |
| **Bridge / cross-chain** | Machine-checked escrow solvency for the two core contracts (`balance >= totalLocked + totalBond`; `balance >= committedLiabilities`), proved inductive in z3; funds route only to allowlisted solver / depositor / own-bond / sink, never to an arbitrary address. | That an allowlisted solver cannot claim-without-filling within a challenge window (the fixed optimistic bond does not scale with value at risk; Phase-1 safety rests on the permissioned, Foundation-vetted solver allowlist); correct accounting under fee-on-transfer / rebasing input tokens on the SpokePool. |
| **Accounts / AA** | ERC-4337 v0.7, EIP-7702, passkey (P-256), Falcon-512, hybrid ECDSA+Falcon, and t-of-n threshold accounts; threshold duplicate-key fix machine-checked and deployed (PQC registry V2); hardened V2 paymasters. | That a classical ECDSA-only or P-256-only account is post-quantum (only Falcon / hybrid accounts are); device / WebAuthn security (out of chain scope); that the founder-gated account-factory V2 redeploy has occurred (buggy factory is live but empty). |
| **Economic core** | Ownerless and immutable AereSink and sAERE: no admin, no governance, no upgrade proxy, no selfdestruct, no delegatecall, no owner withdraw across the deployed set (repo-wide primitive sweep = 0 each); burn hard-capped at 50 percent of validator reward; fixed 2.8B native supply with no mint path. | A blanket "no admin anywhere on the network" (operational contracts have explicit Foundation roles, see next row); protection against a Foundation-set burn rate anywhere between 0 and the 50 percent cap; economic / price outcomes of the burn-buyback-yield mechanism. |
| **Oracles / compliance** | Explicit, documented, bounded Foundation roles: NavOracle can only propose / dismiss / attest and cannot alter an attested snapshot (24-hour challengeable window, permissionless attest); compliance anchors only zk clearance booleans (no PII on chain); Governor / Timelock are inert. | A decentralized or trustless oracle (NAV is a trusted-attestor model); that the Foundation cannot publish a wrong-but-unchallenged snapshot within a window; correctness of any off-chain screening input; anything about governance once control is transferred beyond the Timelock delay. |
---
## 3. Security Assumptions
The guarantees in Section 2 hold only under the following assumptions. If an assumption is false, the corresponding guarantee does not hold. They are stated plainly so that a reviewer can accept or reject each one on its own terms.
- **A-1 Partial synchrony.** Validator clocks are loosely synchronized and, after some unknown global stabilization time (GST), message delay is bounded. QBFT safety does not depend on this, but liveness does: under asynchrony, progress is only eventual (FLP), and the rare f = 2 boundary stall is a consequence of this assumption, not a defect.
- **A-2 Byzantine bound.** At most 2 of the 7 validators are Byzantine (`f = 2` at N = 7, quorum = 5). Beyond one third Byzantine, BFT safety fails and a fork becomes reachable. This bound is exact.
- **A-3 Honest-operator trust model (the load-bearing assumption).** All 7 validators are Foundation-operated, so the trust model is honest-operator, not economic or decentralized, and the Nakamoto coefficient is approximately 1. Aere's safety today rests on the Foundation not running more than 2 equivocating nodes and not censoring at will. This is disclosed, is the reason the chain is not yet credibly decentralized, and is the assumption an external reviewer should weigh most heavily.
- **A-4 Keccak collision resistance.** Block hashing, Merkle-Patricia state proofs, and commit-seal pre-images assume keccak-256 is collision-resistant.
- **A-5 Proof-system soundness.** The SP1 / Plonky3, RISC Zero, and Halo2 proof systems are sound: a verifying proof implies the asserted statement, except with negligible probability. The on-chain verifiers are assumed to correctly implement these systems (backed by KAT / fixture tests, not by a from-scratch soundness proof).
- **A-6 NIST PQC hardness.** The hardness assumptions behind Falcon (NTRU lattice), ML-DSA / ML-KEM (module lattice), and SLH-DSA (hash-based) are unbroken. Signature-layer post-quantum guarantees are void if these are broken.
- **A-7 Audited primitive library.** The precompiles rely on the correctness of Bouncy Castle (`bcprov-jdk18on-1.83.jar`) for Falcon / ML-DSA / SLH-DSA / SHAKE. A bug in that library is a bug in the precompiles.
- **A-8 Device and WebAuthn security.** Passkey / P-256 account security assumes the device secure element protects the private key and that the WebAuthn ceremony is not subverted on the client. This is outside the chain's control.
- **A-9 BN254 is honest until replaced.** The on-chain ZK verifiers use BN254 pairings, which are Shor-breakable. They are assumed secure against classical adversaries only, and this assumption is explicitly temporary: it stands until the planned direct hash-based STARK verifier (Section 1.3) is made real, audited, and activated. Under a cryptographically-relevant quantum adversary, the ZK verification layer and the QBFT light client offer no guarantee.
- **A-10 Precompile determinism.** Every precompile is a pure function of its input bytes, reads no chain state or nondeterministic source, and every node runs an identical client build, so all nodes compute the identical result and gas charge. A divergence here would be a consensus fault.
- **A-11 No external audit yet.** The guarantees are backed by internal formal verification (z3 SMT: 125 proved properties plus 49 counterexample negative controls across the consensus and contract models at last count; Halmos; Solidity SMTChecker; and 22 invariant-property fuzz suites over burn / economics / custody; exact per-suite counts are reproducible by running the published verification harness, `run_consensus_verification.py` plus the contract SMT suite) and by adversarial self-review, but not by an independent external audit. This document is the bridge to that audit; until it completes, every guarantee should be read as "internally verified, not externally attested."
---
## 4. Design Rationale ("Why not?")
This section records, for each load-bearing architecture decision, what was chosen, what the credible alternatives were, and why the alternative was rejected. The intent is to make the trade-offs auditable: every choice below has a cost, and the cost is stated. Where a decision reflects a deliberate deviation from Ethereum's current roadmap, the deviation is documented rather than hidden.
### 4.1 Consensus: QBFT, not HotStuff
**Chosen.** QBFT (a Byzantine-fault-tolerant IBFT-family protocol) as implemented in Hyperledger Besu, producing deterministic single-slot finality at a sub-second cadence (~0.5s target block time). At the live validator count N=7, QBFT tolerates f=2 Byzantine or crashed validators, with a commit quorum of 2f+1 = 5 of 7.
**Alternative considered.** HotStuff (and its pipelined variants), which achieves linear per-round communication complexity and is engineered to scale BFT consensus to hundreds of validators.
**Why not HotStuff.** HotStuff's headline advantage is communication complexity that stays linear as the validator set grows large. Aere runs a small, permissioned validator set (7 today, with a roadmap target in the 9 to 21 range). At that scale QBFT's O(N^2) message complexity is a non-issue in absolute terms (the crypto and gossip cost of a 5-of-7 quorum verify is on the order of a few milliseconds per block, a small fraction of the 500ms slot). The scaling regime where HotStuff wins is one Aere does not operate in and does not plan to enter under this design. Against that non-benefit, QBFT offers three concrete advantages at our size: (1) it provides deterministic, instant, single-block finality (no probabilistic or multi-round confirmation), which is the settlement guarantee the whole network is built to sell; (2) it is mature and already integrated in Besu, so it carries no new-protocol implementation risk; and (3) it is the protocol our post-quantum consensus research, our second-client work, and our zk finality proofs are all built against. Adopting HotStuff would have meant taking on the risk and integration cost of a less-battle-tested (for our client) protocol to buy scalability we have no use for. The honest limitation of the QBFT choice is disclosed in the threat model: at N=7 the fault tolerance is real against node outages but the validators are all Foundation-operated, so decentralization, not consensus mechanism, is the binding trust assumption.
### 4.2 Execution client: forked Besu, not Reth, with Nethermind as the second client
**Chosen.** A fork of Hyperledger Besu v26.4.0 (Java, base commit d2032017, built on a JDK 21 toolchain) as the sole live mainnet producer. The fork adds native post-quantum precompiles and an extended EIP-2935 block-hash lookback (live on mainnet since block 9,189,161) and carries the base-fee-floor change (live since block 10,141,734). A patched Nethermind 1.39.0 (.NET) is the second, independent client.
**Alternatives considered.** Reth (Rust, high-performance) as the base client; or running a single client indefinitely.
**Why not Reth.** Two reasons, one structural and one empirical. Structurally, Besu already ships QBFT, and QBFT block production is Besu-specific: no off-the-shelf Geth, Reth, Erigon, or stock Nethermind produces QBFT-compatible blocks. Choosing Reth would have meant building QBFT production into a client that has no notion of it, before we could produce a single block. Besu also already carried the codebase into which our PQC precompile fork and base-fee changes were integrated. Empirically, a single producing client is a demonstrated systemic risk: the September 2025 Reth halt is the reference case for why a chain that depends on one producing implementation can stop when that implementation hits a bug. That event is precisely the risk the second-client program exists to retire.
**Why Nethermind as the second client (not a second Besu).** A second Besu instance is not client diversity; it shares every bug with the first. Nethermind was chosen specifically for implementation diversity: it is a different codebase in a different language (.NET versus Besu's Java), so a consensus-relevant bug in one is unlikely to exist in the other. The patched Nethermind validates the live chain, runs the five PQC precompiles byte-for-byte identically (27 of 27 NIST KAT vectors), and, on an isolated test network, produces byte-identical QBFT blocks that stock Besu accepts through full BFT validation (with a one-byte-tampered seal rejected). The honest boundary: this cross-client production is proven on an isolated testnet (offline import and, in later work, live devp2p between test nodes), and a listen-only Nethermind follower has been run against the live network; it is not yet a live gossiping producer on chain 2800. Making it one is a founder-supervised change, because a producer bug on a live chain can halt or fork it. Besu remains the sole live producer today; the second client is a client-diversity cross-check and fail-safe, not a second live validator yet.
### 4.3 Interoperability: Hyperlane-compatible, not IBC, plus a zk-interop path
**Chosen.** An EVM-native, Hyperlane-compatible messaging layer (a Mailbox-compatible `AereMessenger`, an Interchain Gas Paymaster `AereIGP`), an ERC-7683 intent layer, and an Across-v3-compatible spoke pool. Alongside this sits a zero-knowledge interop path: on-chain zk light clients that verify a counterparty's consensus finality (inbound Ethereum sync-committee finality, outbound Aere QBFT finality) from inside a contract, via SP1 Groth16 proofs.
**Alternative considered.** IBC (the Inter-Blockchain Communication protocol), the trust-minimized, light-client-based standard from the Cosmos ecosystem.
**Why not IBC.** IBC's design philosophy, verifying the counterparty's consensus rather than trusting a bridge committee, is the right one, and Aere adopts that philosophy directly. But IBC as a protocol is built around Tendermint-style light clients and a non-EVM connection, channel, and packet model. Adopting it natively would have meant importing a substantial non-EVM stack and its tooling into an EVM chain whose entire value proposition is that it is an ordinary EVM. Instead Aere splits the problem: for permissionless, developer-familiar EVM interop it uses the Hyperlane-compatible interfaces (which the EVM tooling ecosystem already speaks), and for the trust-minimization that is IBC's real contribution it built the zk-interop light clients, which capture the "verify the counterparty's consensus, do not trust a relayer" property without leaving the EVM world. The honest boundary: the Hyperlane-compatible endpoints are interface-faithful and deployed, but a live decentralized relayer and interchain-security-module network is not yet stood up (the current bridge path runs through a single Foundation signer with a relayer pending), and the zk light clients are trust-minimized, not trustless, and not quantum-safe (their committed seals are classical secp256k1 and the proof wrap is Groth16 over BN254).
### 4.4 Post-quantum signatures: Falcon and ML-DSA together, not one lattice scheme
**Chosen.** Native on-chain verification of multiple post-quantum signature schemes, including two distinct lattice families: Falcon (Falcon-512 and Falcon-1024) and ML-DSA (Dilithium2 / ML-DSA-44), alongside hash-based schemes (SLH-DSA, XMSS). The precompiles for SHAKE256, Falcon-512, Falcon-1024, ML-DSA-44, and SLH-DSA-SHA2-128s are live on mainnet in the 0x0AE1 to 0x0AE5 band.
**Alternative considered.** Standardizing on a single lattice signature scheme (for example ML-DSA alone, the NIST primary lattice standard, or Falcon alone for its compact signatures).
**Why not a single scheme.** Cryptographic-risk diversification. Falcon and ML-DSA rest on different structured-lattice problems (Falcon on NTRU lattices, ML-DSA on module lattices with Fiat-Shamir-with-aborts). Supporting both means that a cryptanalytic break or a standardization flaw in one lattice family does not, by itself, remove Aere's ability to verify post-quantum signatures: accounts and settlement authorization can migrate to the surviving family. This is the same conservative posture as the hybrid `AereHybridAuth` account, which requires both a classical ECDSA and a Falcon-512 signature so that neither a broken curve nor a broken lattice alone suffices to forge. The cost of supporting multiple schemes is more verifier surface to implement, validate, and eventually audit; that cost is accepted deliberately in exchange for not betting the network's post-quantum future on a single mathematical assumption. The critical honest scope, stated wherever post-quantum is discussed: this is post-quantum at the signature, account, and application layer only. Consensus remains classical (validators sign secp256k1 QBFT messages) and the zk verification is classical (BN254 Groth16, Shor-breakable). Aere never claims post-quantum consensus.
### 4.5 Account abstraction: ERC-4337, not native AA, with EIP-7702 as the bridge
**Chosen.** ERC-4337 (v0.7) smart accounts as the account-abstraction path, with EIP-7702 (live under the Pectra ruleset) as the migration bridge that lets existing externally-owned accounts (EOAs) delegate to smart-account code.
**Alternative considered.** Native, protocol-level account abstraction, up to and including the "all accounts are smart accounts by default" end-state.
**Why not native AA.** ERC-4337 needs no consensus change and is an established standard with an existing tooling and bundler ecosystem; it delivers programmable accounts (including the Falcon-owned post-quantum smart account, `AerePQCAccountFactory`) without touching the protocol, which preserves exact EVM and Ethereum compatibility. Making all accounts smart by default would break EOA and EVM compatibility: it changes what an account fundamentally is, which breaks the assumptions of existing tooling, existing contracts, and existing user keys. Preserving that compatibility was a deliberate design goal, not an oversight. EIP-7702 is the chosen bridge precisely because it upgrades existing EOAs in place (an EOA can delegate to smart-account code) without forcing the disruptive "everything is a contract" change. Note on the Ethereum roadmap lineage: EIP-3074, an earlier EOA-empowerment proposal, was superseded by EIP-7702, which is the mechanism Aere carries; Aere does not implement 3074.
### 4.6 The virtual machine: extend the EVM, do not build a new VM
**Chosen.** Keep the EVM. Solidity, EVM bytecode, JSON-RPC, and the developer tooling are all unchanged. Differentiation (post-quantum verification, parallel execution, the block-hash lookback) is added as precompiles and client fork rules at otherwise-empty addresses, not as a new instruction set or a new language.
**Alternative considered.** Designing a new, purpose-built virtual machine (a new bytecode, a new language, or a non-EVM execution environment) optimized around Aere's differentiators.
**Why not a new VM.** A new VM maximizes both technical risk and adoption risk. Technically, a mature VM has years of implementation hardening, a known cost model, and a formal-ish operational history; a new VM starts that clock at zero. For adoption, the EVM is the single largest smart-contract developer surface in existence, and every deviation from it (a nonstandard opcode, a nonstandard account model, a nonstandard RPC) is a tax on every developer and every tool. Aere's thesis is settlement, not a novel programming model, so the design keeps the mature, tested execution environment intact and adds its differentiation at the edges where the EVM has explicit, standard extension points (precompiles at reserved addresses, activated by client-only forks in the same manner Ethereum activates its own hard forks). The result: nothing about Aere's account, passkey, or post-quantum surface depends on a nonstandard EVM. The cost of this choice is that some ambitions (base-layer parallel execution, an EVM-in-a-zkVM) must be reached inside the existing model rather than by designing them in from scratch; those remain roadmap and are labeled as such.
### 4.7 The burn: a cut of the validator reward, not a base-fee burn
**Chosen.** Aere burns a share (37.5% under the current configuration) of the validator coinbase block reward, applied by a splitter contract (`AereCoinbaseSplitterV2`) that validators or their forwarder daemons call each block. The rate is a Foundation-adjustable parameter, hard-capped at 50% (5000 basis points). It is explicitly not EIP-1559 protocol-level base-fee burning.
**Alternative considered.** An EIP-1559-style base-fee burn enforced by consensus (the mechanism Ethereum mainnet uses, where the base fee of every transaction is burned as a protocol rule).
**Why not a base-fee burn.** Being precise here is a credibility matter, and the design is deliberately honest about it. A consensus-level base-fee burn is a protocol rule; the Aere burn is a coinbase-reward split applied above consensus by a contract. The design routes the burn through the coinbase layer rather than enshrining it in consensus for two reasons: it keeps the burn out of the consensus-critical path (a change to the burn rate cannot halt or fork the chain, whereas a change to a consensus base-fee rule is a hard fork), and it lets the destinations be made immutable at the contract layer while the rate stays a bounded, governable dial. What is sealed is not the rate but the endpoints: `AereSink` (no owner, no setters, immutable splits) and `AereFeeBurnVault` (no owner, no admin, no withdraw function), so burned value cannot be rugged or drained regardless of who sets the rate. This document must not describe the Aere burn as a base-fee burn; it is a validator-reward-cut burn, rate-adjustable within a 50% ceiling, into immutable destinations. Separately, and not to be conflated with the burn: Aere does run a base-fee change at the protocol layer, a 1-Gwei EIP-1559 base-fee floor (live since block 10,141,734). That floor sets a minimum base fee; it is not a burn mechanism.
### 4.8 Deliberate compatibility deviations from the Ethereum roadmap
Aere tracks Ethereum's hard forks (Pectra activated at block 2,075,363; Fusaka / Osaka at block 2,106,606) so that its EVM surface matches mainnet at the same addresses. Several roadmap features are, however, deliberately neutralized or absent because Aere is a QBFT chain with no separate consensus (beacon) layer and no blob data-availability market. These are design decisions, disclosed here so an integrator is not surprised:
- **Base fee has a floor plus a validator-reward burn**, not a consensus base-fee burn (see 4.7). The floor is 1 Gwei, live from block 10,141,734.
- **EIP-4844 blob transactions are present in the EVM, but `BLOBBASEFEE` (EIP-7516) returns 0** because there are no blobs: Aere runs no blob data-availability market.
- **EIP-4788 (parent beacon block root) returns 0x0**, because a QBFT chain has no separate beacon / consensus layer to expose a root from.
- **EIP-3074 is not implemented; it is superseded by EIP-7702**, which is the EOA-delegation primitive Aere carries (see 4.5).
- **PeerDAS (EIP-7594), FOCIL, and EOF have no active role under Aere's QBFT model.** PeerDAS has no data-availability role (there is no blob market), FOCIL (proposer inclusion lists) has no fork-choice or proposer-inclusion layer to enforce under QBFT, and EOF is not activated under the current ruleset.
---
## 5. Failure Analysis
This section describes Aere's behavior under each significant failure mode. For each, it states whether the behavior is PROVEN (demonstrated by a repeatable test or a live event) or EXPECTED (the deterministic consequence of the protocol's rules, not separately soak-tested at that exact configuration), and whether the chain halts safely, forks, or continues. The governing consensus parameters are fixed: N=7 validators, f=2, commit quorum 2f+1 = 5 of 7.
A safety-first summary that holds across every mode tested: **in all adversarial fault testing to date, zero forks were observed.** The chain's failure posture is to halt (and later recover) rather than to fork. That is the property a settlement layer must have, and it is the property the evidence supports.
| Failure mode | Outcome | Fork? | Status |
|---|---|---|---|
| 1 validator dies | Chain continues | No | PROVEN |
| 2 validators offline | Continues at the quorum limit (rare, self-recovering stalls) | No | PROVEN |
| 3+ validators offline | Chain halts (safe) | No | EXPECTED (quorum math) + sub-quorum halt demonstrated |
| Network partition | Minority side halts, recovers on reconnect | No | PROVEN (halt-then-recover demonstrated) |
| Nethermind and Besu disagree on state | Divergence detected by client cross-check | No (fail-safe) | PROVEN as a validation cross-check; not yet a live automated gate |
| PQC precompile errors on input | Fail-closed (defined empty / zero output, no revert) | No | PROVEN (live behavior verified) |
| Bridge goes down | Cross-chain messaging halts; L1 keeps running | No | EXPECTED (app-layer isolation) |
| ZK prover unavailable | New proofs pause; chain and verified state keep running | No | EXPECTED (advance is manual / permissionless) |
### 5.1 One validator dies
**Behavior.** The chain continues. With one of seven validators down, six remain, which exceeds the commit quorum of five, so blocks continue to be proposed (round-robin skips the dead proposer's slots via round change) and committed.
**Halt / fork / continue.** Continues. No fork.
**Status.** PROVEN. Single-fault survival is demonstrated in the fault-tolerance work: in an N=4 (f=1) mixed-client run, killing one validator left the surviving three committing; the pure-Besu N=7 baseline sustains block production well inside the f=2 boundary. This is also the direct consequence of quorum math (six alive > five needed).
### 5.2 Two validators offline
**Behavior.** The chain continues, but it is at the exact fault-tolerance limit: with two of seven down, exactly five validators remain, which equals the quorum of five with zero slack. All five survivors must prepare and commit every block. In a 40-cycle adversarial soak (kill two random validators, measure a 45-second fault window, restart, measure recovery), the chain committed blocks in 37 of 40 cycles and, in 3 of 40 cycles, produced a temporary zero-progress ("stall") window. Those three stalls were temporary and self-recovering, not permanent halts and not forks: every stalled cycle resumed the moment the killed nodes restarted, with `fork_during = False` and `resynced_after = True` in all cases.
**Halt / fork / continue.** Continues at the limit. No fork in any of the 40 cycles. Under specific worst-case two-down proposer arrangements at the exact f=2 boundary, progress can pause briefly, but the chain is neither forked nor permanently stuck.
**Status.** PROVEN, with the boundary nuance reported honestly rather than rounded to "zero halts." Evidence: `FAULT-TOLERANCE-CHARACTERIZATION-2026-07-14.md` (0 forks / 40, 40/40 killed nodes resynced, 3/40 temporary self-recovering stall windows; pure-Besu N=7 kill-two reference showed 0 forks and 0 stalls over 20 cycles). The rare stall at the boundary is inherent to QBFT running with zero quorum slack, not a fork and not a safety failure.
### 5.3 Three or more validators offline
**Behavior.** The chain halts. With three of seven down, only four validators remain, which is below the commit quorum of five, so no block can reach quorum and progress stops. It halts cleanly: it does not fork, because no side can form a quorum, and it resumes exactly where it stopped once enough validators return.
**Halt / fork / continue.** Halts, safely. No fork.
**Status.** EXPECTED as the deterministic consequence of quorum math (four alive < five needed), and the sub-quorum halt-then-recover behavior itself is demonstrated (see network partition, below, and the hybrid-consensus negative controls where dropping the set below quorum halted block production in place and recovered on restoration with no re-genesis and no split). This specific "third of seven goes down" configuration follows directly from the quorum rule; it is not separately soak-characterized as its own experiment, and the claim as written rests on quorum math plus the demonstrated sub-quorum halt-and-recover.
### 5.4 Network partition
**Behavior.** On a network partition, the side of the partition that does not hold a quorum (at least five of seven validators) cannot commit blocks and halts. It does not fork: a minority partition can never form the 5-of-7 committed-seal quorum required to finalize a block, so it produces no competing finalized chain. When the partition heals and the validators reconnect, the halted side resumes and resyncs to the canonical head. This halt-then-recover behavior was demonstrated directly.
**Halt / fork / continue.** The minority side halts; the majority side (if one partition holds >= 5 validators) continues; on reconnection the halted side recovers. No fork.
**Status.** PROVEN (halt-then-recover demonstrated). Evidence: sub-quorum halt and in-place recovery were shown in the in-place PQC activation work (dropping below the required quorum halted block production at a specific height, and restoring the missing signers resumed from that exact height with no re-genesis) and in the hybrid-consensus negative controls (killing enough validators to break quorum halted the chain with no split and recovered in place). The single-operator caveat applies and is disclosed: all seven validators are Foundation-operated on a single cloud provider, so a partition that is really an infrastructure or operator outage is a correlated failure, not an independent one. QBFT's partition safety (no fork) holds regardless; the availability of a quorum-holding side is an operational, not a protocol, guarantee at N=7 under one operator.
### 5.5 The two clients disagree on state (Nethermind vs Besu)
**Behavior.** The second client exists precisely to catch this. The patched Nethermind independently re-derives state: it has re-validated the full chain to genesis with zero invalid seals and re-executed real chain-2800 blocks to byte-for-byte identical block hashes and state roots (spot-checked across many heights, and, as a live listen-only follower, matched the live RPC's hash and state root at every checked height in range). Because there is exactly one definition of correct state and two independent implementations computing it, a consensus-relevant divergence between Besu and Nethermind (a differing state root, a differing block hash, a differing seal decision) is detectable by comparing the two: it does not silently pass.
**Halt / fork / continue.** This is a fail-safe (a cross-check), not an automatic consensus action. A detected divergence is a signal to halt and investigate before the divergent block is trusted, not something that itself forks or halts the live chain. Besu is the sole live producer today, so a Nethermind disagreement flags a discrepancy against the producer; it does not currently arbitrate consensus.
**Status.** PROVEN as a validation cross-check (full-chain re-validation, byte-identical state-root re-execution, and identical PQC precompile outputs). It is not yet a live, automated, alerting divergence gate wired into production monitoring, and Nethermind is not yet a live producing validator. Framed honestly: the second client demonstrably would detect a state divergence, and this is real client-diversity assurance; turning that into an automated live fail-safe gate is the remaining operational work.
### 5.6 A PQC precompile errors on its input
**Behavior.** The precompiles fail closed. On malformed or unexpected input they return a defined non-accepting output rather than reverting: the Falcon, ML-DSA, and SLH-DSA verifier precompiles (0x0AE1 through 0x0AE4) return a 32-byte zero word (which reads as "not verified"), and the SHAKE256 precompile (0x0AE5) returns empty on malformed framing. None of them revert, and none of them can affect consensus: they are pure verification functions at reserved addresses, so a bad input produces a not-verified result and nothing more. The integrator rule that travels with this is explicit: treat a zero-word or empty return as not-verified, and validate input lengths yourself.
**Halt / fork / continue.** The chain is unaffected. No revert of consensus, no halt, no fork. The calling contract simply sees a not-verified result.
**Status.** PROVEN. This as-shipped behavior was verified against the live chain and reconciled into the precompile documentation (the earlier draft's "MUST revert" framing was corrected to the actual fail-closed zero-word / empty-output behavior). Evidence: the AIP reconcile in `aerenew/research/aip-draft-pqc-precompiles.md` "As shipped" corrections.
### 5.7 The bridge goes down
**Behavior.** Cross-chain messaging stops; the L1 keeps running. The interop layer (the Hyperlane-compatible messenger, the intent and spoke contracts, and any relayer) sits entirely at the application layer and is not in the consensus or execution-critical path. If the bridge relayer or a counterparty deployment goes offline, outbound and inbound cross-chain messages stop being delivered, but Aere chain 2800 continues producing and finalizing blocks exactly as before.
**Halt / fork / continue.** The L1 continues. Only cross-chain message delivery halts. No fork.
**Status.** EXPECTED, by architectural isolation: the bridge is app-layer and the current bridge path is not even a live decentralized network yet (it runs through a single Foundation signer with a relayer pending), so its being down affects only cross-chain product surface, never L1 liveness.
### 5.8 The ZK prover is unavailable
**Behavior.** New proofs pause; the chain and all existing verified state keep running. The zk components (the rollup-validity anchor, the inbound Ethereum light client, the outbound QBFT light client) advance only when someone submits a proof: advancing is permissionless but not automatic. If the off-chain prover (the SP1 prover, run on dedicated non-infra hardware) is unavailable, no new finality advances or new validity proofs are recorded, but nothing already verified is rolled back, and the L1 itself does not depend on the prover to produce or finalize blocks.
**Halt / fork / continue.** The L1 continues. Only the production of new zk proofs pauses; previously verified state stands. No fork.
**Status.** EXPECTED, by design: the light clients' heads move only when a proof is submitted ("permissionless but not automatic"), and proving runs off-chain, off the infra box, so prover downtime cannot halt consensus. The honest boundary already stated for the zk path applies: it is trust-minimized, not trustless, and not quantum-safe (Groth16 over BN254).
---
## 6. Document Status and Reproducibility
This specification is the bridge between Aere's internal verification and an independent external audit. Its guarantees are backed by machine-checked formal verification and adversarial self-review, and every figure in it is intended to be reproducible from the published repositories and harness (that reproducibility is itself a roadmap item, tracked as the open-benchmark and reproducible-build work). Two standing honesty rules govern any use of this document:
1. Until an independent external audit completes, every guarantee reads as "internally verified, not externally attested."
2. The post-quantum scope boundary is absolute: signatures and accounts can be post-quantum; consensus (ECDSA / QBFT) and the on-chain ZK verifiers (BN254 Groth16) are classical. The chain as a whole is not post-quantum, and no derived material may claim otherwise.

125
AERE-ENTROPY-BEACON-FIX.md Normal file
View File

@ -0,0 +1,125 @@
# Spec 9: Aere entropy honesty fix — real drand BLS verification
## The gap (what was dishonest)
`aerenew/contracts/contracts/oracle/AereRandomnessBeacon.sol` (live-deployed at
`0x25b6317efD8C7d425210F56Ee1E204852CD8213C`) shipped with a placeholder
verifier:
```solidity
function _verify(uint64 /*round*/, bytes calldata /*signature*/) internal pure returns (bool) {
// Phase 2 implementation goes here.
return true;
}
```
It verified nothing. Worse, `strictVerifyEnabled` defaults to `false`, so even
that stub was never called: any bytes could be submitted as a "drand round". It
also derived randomness as `keccak256(signature)`, which is NOT drand's
canonical randomness (`SHA-256(signature)`).
## What I built
New files (the live beacon was NOT modified):
- `aerenew/contracts/contracts/oracle/AereRandomnessBeaconV2.sol`
- `library AereDrandQuicknetBLS` — real BLS12-381 verification on EIP-2537:
- `messageForRound` = `SHA-256(round as 8-byte big-endian)` (quicknet is UNCHAINED)
- `hashToField` = RFC 9380 `expand_message_xmd` (SHA-256, len 128) + reduce
mod p via MODEXP. Uses only SHA-256 (0x02) + MODEXP (0x05), so it runs on
any EVM.
- `hashToG1` = RFC 9380 SSWU_RO to G1 via `BLS12_MAP_FP_TO_G1` (0x10) +
`BLS12_G1ADD` (0x0b). (map already clears the cofactor, and cofactor
clearing is a homomorphism, so `map(u0)+map(u1)` equals the RFC result.)
- `decompressG1` = zcash/ETH2 compressed-G1 decompression via MODEXP sqrt
(p ≡ 3 mod 4, sqrt = a^((p+1)/4)); consumes drand's exact 48-byte sig.
- `verifyRoundCompressed` = pairing check `e(H(m),pk) * e(sig,-G2) == 1` via
`BLS12_PAIRING_CHECK` (0x0f).
- `contract AereRandomnessBeaconV2``submitRound` ALWAYS verifies (no phase
flag, no admin off-switch); stores canonical `SHA-256(signature)` randomness.
- `aerenew/contracts/test/AereRandomnessBeaconV2.test.js` — real drand vectors.
- `aerenew/contracts/hardhat.config.prague.js` — pins the local `hardhat`
network to the `prague` hardfork so EIP-2537 is available for the full test.
**Fail-closed everywhere:** any wrong-length input, absent precompile (empty
returndata), wrong-length precompile output, non-square x, point at infinity, or
failed pairing resolves to `false`. There is no path that returns `true` without
a genuine pairing equality. When EIP-2537 is absent, verification returns
`false` and `submitRound` reverts (proven by the fail-closed-absence test).
## drand chain targeted (pinned, not guessed)
League of Entropy **quicknet**, scheme `bls-unchained-g1-rfc9380` (confirmed live
at `api.drand.sh/<chainhash>/info`):
- chain hash: `52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971`
- public key (G2, 96-byte compressed):
`83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a`
- period 3 s, genesis 1692803367, unchained, sigs on G1 / pubkey on G2
- hash-to-curve DST: `BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_`
All scheme parameters were proven correct BEFORE writing Solidity, by
independently verifying genuine round 1000000 with `@noble/curves` (pairing of
`H(m)` against the pubkey equals pairing of the signature against the G2
generator → true; wrong round → false).
## Real test vectors used (genuine, from api.drand.sh)
- round 1000000: sig `83ad29e4...abe72`, randomness `b22aad47...440af3`
- round 1000001: sig `a5bd91e5...c4218`, randomness `9f45f439...84ff`
(used as a valid-but-wrong-round negative case)
Confirmed on-chain: `SHA-256(sig)` reproduces the published randomness for both.
## Test result (REAL, not [MEASURE])
Run from `aerenew/contracts`:
1. Default config (`cancun`, no EIP-2537):
`npx hardhat test test/AereRandomnessBeaconV2.test.js`
-> **11 passing, 5 pending**. Message reconstruction, RFC 9380 hash-to-field
(matches @noble reference), G1 decompression (matches @noble reference),
canonical randomness, input validation, and the fail-closed-absence checks all
pass. The pairing cases correctly report `[MEASURE]` and skip because the
precompiles are absent.
2. Prague config (EIP-2537 live in the local EDR EVM):
`npx hardhat test test/AereRandomnessBeaconV2.test.js --config hardhat.config.prague.js`
-> **15 passing, 1 pending**. Full on-chain BLS verification:
- hash-to-curve to G1 matches the independent @noble reference
- genuine round 1000000 verifies **true**; genuine round 1000001 verifies **true**
- valid signature submitted for the wrong round verifies **false**
- tampered signature verifies **false**
- `submitRound(1000000, genuineSig)` stores randomness == published
`b22aad47...440af3`; double-submit reverts; a non-verifying signature reverts
fail-closed with no storage.
The local EDR EVM at the `prague` hardfork implements the same EIP-2537 spec that
is "Supported" on Aere mainnet (per `docs/AERE-EIP-COMPATIBILITY-MATRIX.md`), so
this is a genuine end-to-end pass of the on-chain verification logic.
## Honest status
- This is a BUILT-AND-TESTED replacement, NOT a live swap. The deployed beacon
was not touched. Activating V2 (or wiring real verification into the live
contract) on Aere mainnet is founder-gated.
- [MEASURE, optional] A final confirmation run against the actual Aere mainnet
RPC (chain 2800) would close the loop that mainnet's EIP-2537 matches the
local EDR semantics. The compatibility matrix lists 2537 as Supported; the
logic itself is proven locally on an EIP-2537 EVM.
- QUANTUM SCOPE: drand's BLS12-381 is CLASSICAL cryptography and is
Shor-breakable. This fix makes the beacon actually verify what it stores (a
correctness + honesty fix). It does NOT make the randomness post-quantum and
is unrelated to Aere's PQC precompile track (Falcon/ML-DSA/SLH-DSA).
## [VERIFY] flags
- None on the drand scheme parameters: the DST, message construction, and G1/G2
placement are all confirmed by the genuine round verifying true both in
@noble/curves and in the on-chain pairing.
- The embedded `PUBKEY_G2` and `NEG_G2_GENERATOR` constants are off-chain
decompressions of, respectively, the canonical 96-byte compressed quicknet key
and the standard BLS12-381 G2 generator. They are reproducible by anyone and
are transitively validated by the passing pairing (a wrong pubkey/generator
would make round 1000000 fail). Not a blocking [VERIFY], but flagged as a
precomputed constant for an auditor to re-derive.

View File

@ -0,0 +1,59 @@
# Spec 8 - ERC-7683 Destination Settler for Aere Network
## What was built (all new files, deployed origin contracts untouched)
1. `aerenew/contracts/contracts/intents/AereDestinationSettler.sol`
- Implements ERC-7683 `IDestinationSettler.fill(bytes32 orderId, bytes originData, bytes fillerData)`
from the existing `contracts/intents/IERC7683.sol` (reused, not redefined).
- A solver (msg.sender = filler) delivers the promised output straight to the recipient
on Aere via OpenZeppelin SafeERC20 (no custody), records the fill, emits `Filled`.
- `originData` = the origin's declared output leg, matching AereSpokePool `_buildResolved`:
`abi.encode(bytes32 orderId, address outputToken, uint256 outputAmount, bytes32 recipient)`.
- `fillerData` = `abi.encode(address deliveredToken, uint256 deliveredAmount,
address deliveredRecipient, bytes32 repaymentAddress)`.
- Fail-closed reverts: `OrderIdMismatch`, `ZeroRecipient`, `ZeroAmount`/`ZeroAddress`,
`AlreadyFilled` (duplicate), `OutputMismatch` (wrong token/recipient or short amount).
- Views: `fills`, `getFill`, `isFilled`, `fillCount`, and `fillCommitment(orderId)`
(canonical repayment binder = keccak256 over AERE_CHAIN_ID=2800, settler, orderId,
filler, outputToken, outputAmount, recipient, repaymentAddress).
- nonReentrant; checks-effects-interactions.
2. Finality / repayment wiring (real reference, not a live cross-chain claim):
- Declares `IAereQbftFinalityLightClient` matching the deployed
`contracts/interop/AereZkQbftLightClient.sol` (0xCaDA54FA...6488): the SP1 Groth16
QBFT-finality prover (guest `qbft-lightclient-guest`, quorum ceil(2N/3) = 5-of-7).
- `FILLED_EVENT_SIG` + `Filled` log + `fillCommitment` are the on-chain surface an
origin-side `AereOutboundVerifierV2`-style verifier consumes: it proves the `Filled`
log was included in a QBFT-final Aere block (validator-set anchored) and repays the
solver, no trusted relayer.
- The light client is deployed on the consuming/origin chain (Aere is native on Aere),
so the settler does NOT call it on-chain; documented, not faked.
3. `aerenew/contracts/test/destination-settler.test.js` - RAN, 4 passing:
- valid fill: delivers output, records fill, emits Filled, fillCommitment matches
- duplicate fill reverts (AlreadyFilled)
- mismatched-output reverts (OutputMismatch) - tested short amount, wrong recipient, wrong token
- zero-recipient reverts (ZeroRecipient)
- Command: `npx hardhat test test/destination-settler.test.js` from aerenew/contracts.
4. `aerenew/docs/AERE-ERC7683-DESTINATION.md` - origin+destination composition, solver
flow (lock on origin, fill on destination, prove-and-repay), proof shape, honest status,
and the point that solver liquidity removes the bridge-liquidity bootstrap.
## REAL test result
Compiled 1 Solidity file successfully. 4 passing (~1s), 0 failing.
## Flags
- [MEASURE] End-to-end cross-chain repayment (off-chain SP1 prover over the
qbft-lightclient-guest ELF + origin-chain verifier deploy on a 2nd chain) is NOT
exercised on one chain in the unit test. No live cross-chain flow claimed.
- [VERIFY] Exact receipt-trie inclusion encoding for the Filled log: confirm against the
final AereOutboundVerifierV2 guest.
- [VERIFY] ERC-7683 struct shapes taken from the repo's vendored IERC7683.sol (canonical
Input/Output/ResolvedCrossChainOrder/IDestinationSettler); matches AereSpokePool origin side.
## Scope / honesty
Settlement/interop only. Aere consensus stays classical secp256k1 ECDSA QBFT; the finality
proof is ABOUT that QBFT finality, verified classically (falconVerified required false).
Deployed origin contracts (AereSpokePool, settlement hub) not modified. No em-dashes; brand
"Aere Network"/"Aere", ticker AERE; solc 0.8.23.

188
AERE-ERC7683-DESTINATION.md Normal file
View File

@ -0,0 +1,188 @@
# Aere Network ERC-7683 Destination Settler
The receiving side of cross-chain intents on Aere Network. This document explains how
the origin and destination halves compose, the solver flow, the repayment proof path,
and the honest status of what is on-chain today versus what needs the off-chain solver
and finality prover.
Contract: `aerenew/contracts/contracts/intents/AereDestinationSettler.sol`
Interface: ERC-7683 `IDestinationSettler` (`contracts/intents/IERC7683.sol`)
Test: `aerenew/contracts/test/destination-settler.test.js` (4 passing)
## Why a destination settler at all
Aere Network already ships the ORIGIN side of ERC-7683: `AereSpokePool`
(`contracts/intents/AereSpokePool.sol`) is an `IOriginSettler`. A user locks input
tokens on the origin chain and emits an `Open` event that declares the OUTPUT leg they
want on the destination chain (token, amount, recipient). What was missing is the
DESTINATION half: the contract a solver calls to actually deliver that output and to
record the delivery so the solver can be repaid. `AereDestinationSettler` is that half.
An intent design like this removes the need to bootstrap bridge liquidity. A classic
lock-and-mint bridge must pre-fund destination-side liquidity before it can serve a
single transfer. An intent bridge does not: the SOLVER fronts their own liquidity to
fill the order, and is repaid the locked input on the origin chain after the fill is
proven final. Aere Network therefore never has to seed or custody bridge liquidity; a
competitive market of solvers supplies it per intent.
## How origin and destination compose
```
ORIGIN CHAIN AERE NETWORK (destination, chain 2800)
------------ --------------------------------------
User -> AereSpokePool.open()
locks input, emits Open
(declares output leg) ........> Solver reads Open, calls
AereDestinationSettler.fill(orderId,
originData, fillerData)
- delivers output to recipient
- records the fill
- emits Filled
(fill is included in an Aere block)
|
Aere QBFT finalizes the block
|
Origin-side verifier <........ SP1 proof: "this Filled log was included
(AereOutbound- in a QBFT-final Aere block", validator-set
VerifierV2 pattern) anchored (off-chain prover + on-chain verify)
repays the solver the
locked input
```
`originData` is the origin settler's declared output leg. `AereSpokePool` builds it (see
`_buildResolved`) as:
```solidity
abi.encode(bytes32 orderId, address outputToken, uint256 outputAmount, bytes32 recipient)
```
`fillerData` is the solver's actual delivery plus their origin-chain repayment address:
```solidity
abi.encode(address deliveredToken, uint256 deliveredAmount, address deliveredRecipient, bytes32 repaymentAddress)
```
## The solver flow
1. LOCK on origin. The user calls `AereSpokePool.open()` (or the gasless `openFor` /
`openForPQC`), locking input and emitting `Open` with the declared output leg.
2. FILL on destination. A solver calls `AereDestinationSettler.fill(orderId, originData,
fillerData)` on Aere Network. The settler pulls the output token from the solver and
delivers it straight to the recipient (it never custodies the output), records the
fill, and emits `Filled`.
3. PROVE and REPAY on origin. Once the fill's block is QBFT-final on Aere, an SP1 proof
of finality plus log inclusion is generated off-chain and submitted to an origin-side
verifier, which repays the solver the locked input. No trusted relayer is required;
the proof is validator-set anchored.
## On-chain fail-closed guarantees
`fill(orderId, originData, fillerData)` reverts, and records nothing, when:
| Condition | Error |
| --- | --- |
| `orderId` argument does not equal the id inside `originData` | `OrderIdMismatch` |
| Declared recipient is zero | `ZeroRecipient` |
| Declared output token is zero, or declared amount is zero | `ZeroAddress` / `ZeroAmount` |
| Order already filled once | `AlreadyFilled` |
| Delivered token or recipient does not match the declared output, or delivered amount is short | `OutputMismatch` |
A fill that delivers the wrong token, to the wrong recipient, or short can never be
recorded, and an order can be filled at most once, so a solver can never be repaid twice
for a single intent. Delivery is atomic: the output is pulled from the solver and sent
to the recipient with OpenZeppelin `SafeERC20` in the same call, after the record is
written (checks, effects, interactions; `nonReentrant`).
After a successful fill the settler exposes:
- `fills(orderId)` / `getFill(orderId)`: the full fill record (filler, output token,
amount, recipient, timestamp, block, repayment address).
- `isFilled(orderId)`: whether the order has been filled.
- `fillCommitment(orderId)`: the canonical repayment commitment (see below).
## Repayment proof path (wiring to Aere's QBFT finality)
The origin chain must learn "this exact fill was included in a block that is FINAL on
Aere Network" without trusting a relayer. Aere Network already has the machinery, and
the settler is built to plug into it.
1. FINALITY prover. `contracts/interop/AereZkQbftLightClient.sol` (live on consuming
chains at `0xCaDA54FA...6488`) proves, in the SP1 zkVM (guest
`qbft-lightclient-guest`), that one Aere header is QBFT-final: it decodes the Besu
QBFT `extraData`, recomputes both Besu hash pre-images, ecrecovers each committed
seal, and asserts the distinct-validator count reaches the QBFT quorum `ceil(2N/3)`
(5-of-7 for Aere's live N=7 set), binding to an IMMUTABLE validator-set anchor. The
settler declares this interface as `IAereQbftFinalityLightClient` for reference. This
prover runs on the consuming/origin chain, not on Aere Network (Aere is native there),
so the settler does not call it on-chain.
2. INCLUSION plus delivery. `contracts/interop/AereOutboundVerifierV2.sol` is the
origin-side pattern that CONSUMES such a proof. It accepts an SP1 proof that a
specific Aere log was included in a QBFT-final Aere block (finality plus receipt
inclusion, one guest), binds the proof's committed `validatorSetRoot` to its own
immutable anchor, enforces exactly-once replay, and only then acts. An origin-side
verifier built on this pattern trusts THIS settler as the source address and
`FILLED_EVENT_SIG` as the event, re-derives `fillCommitment` from the proven log, and
repays the solver.
The on-chain surface the settler must provide for that path is the `Filled` event, the
persisted `FillRecord`, and the `fillCommitment` binder. It provides all three.
`fillCommitment(orderId)` binds:
```solidity
keccak256(abi.encode(
AERE_CHAIN_ID, // 2800, matching AereOutboundVerifierV2.AERE_CHAIN_ID
address(this), // this settler
orderId,
filler, // solver, repaid on the origin chain
outputToken,
outputAmount,
recipient,
repaymentAddress // solver's origin-chain repayment address
))
```
### Proof shape
The origin-side proof is exactly the shape `AereOutboundVerifierV2` already documents: an
SP1 Groth16 proof over Aere QBFT finality plus log inclusion, with the guest committing
`validatorSetRoot` into its public values and the on-chain verifier binding that root to
an immutable anchor. Public values are the 288-byte (9 word) `abi.encode` of `(uint64
chainId, uint64 blockNumber, bytes32 blockHash, bytes32 root, uint8 claimKind, bytes32
commitment, bytes32 validatorSetRoot, bool finalized, bool falconVerified)`. The pure
finality proof consumed by `AereZkQbftLightClient` is the narrower 192-byte (6 word)
form. The exact receipt-trie inclusion encoding for this settler's `Filled` log is
[VERIFY: confirm against the final AereOutboundVerifierV2 guest].
## Honest status
ON-CHAIN and tested today (`test/destination-settler.test.js`, 4 passing on Hardhat):
- A valid fill delivers the output to the recipient, records the fill, emits `Filled`,
and exposes the record plus `fillCommitment`.
- A duplicate fill reverts (`AlreadyFilled`).
- A mismatched-output fill reverts (`OutputMismatch`), tested for short amount, wrong
recipient, and wrong token.
- A zero-recipient fill reverts (`ZeroRecipient`).
NOT on one chain, marked [MEASURE]:
- The end-to-end cross-chain flow (off-chain SP1 prover run over the
`qbft-lightclient-guest` ELF, plus the origin-chain verifier deployment that repays the
solver) is NOT exercised by this contract or its unit test. It needs the separate
off-chain prover and a second chain. This is a settlement / interop composition, not a
claim of a live cross-chain transfer.
- The exact receipt-trie inclusion encoding is [VERIFY: confirm against the final
AereOutboundVerifierV2 guest].
SCOPE. This is a settlement / interop-layer contract. It does not touch, gate, or change
Aere Network consensus, which remains classical secp256k1 ECDSA QBFT. The finality proof
above is a proof ABOUT that QBFT finality, verified classically today. That is the
honest and intended posture, not a post-quantum consensus claim. The `falconVerified`
slot in the proof public values is recorded and required to be `false` on mainnet.
Repayment security reduces to the honesty of at least `2f+1` of Aere Network's QBFT
validators (5-of-7 today, all Foundation-operated: an operator assumption, not an
economic one) plus a sound SP1 / Groth16 verifier and the correct validator-set anchor.
Recording a fill here is permissionless and does not, by itself, move any origin funds;
repayment happens only on the origin chain and only behind the proof.

62
AERE-ERC8004-SUMMARY.md Normal file
View File

@ -0,0 +1,62 @@
# Spec 5: ERC-8004 + x402/AP2 conformance for Aere's agent stack
## What was built (all under aerenew/)
Thin ADAPTERS over the LIVE, already-deployed agent primitives. Nothing live was redeployed or modified.
New dir: `aerenew/contracts/contracts/erc8004/`
- `IERC8004.sol` - modeled ERC-8004 Identity / Reputation / Validation interfaces ([VERIFY] flagged).
- `AereIdentityRegistry8004.sol` - Identity adapter over the deployed AereAgentDID (0xce64..22C5).
agentId == the DID's Falcon root key id; register binds AgentCard URI + domain to an existing DID agent,
controller-gated; resolve by id/domain/address; DID string did:aere:<chainId>:<agentId>.
- `AereReputationRegistry8004.sol` - Reputation adapter over the deployed AereAIReputation (0x781e..feBF).
READ path (getScore/getStats) reads the live scoreOf/statsOf directly. WRITE path (giveFeedback) forwards
to live attest; needs the adapter to be a registered attestor (live set is immutable Foundation-only, so
write-through is a founder-gated governance step -> honest AdapterNotAttestor revert + canWriteThrough()).
- `AereValidationRegistry8004.sol` - THE DIFFERENTIATOR. ERC-8004 validation request/respond where the
response proof is a Falcon-512 or ML-DSA-44 signature verified on-chain via the live precompiles
(0x0AE1 / 0x0AE3) through AerePQCKeyRegistry.verifyWithKey. Fail-closed, non-replayable, challenge-bound.
- `AereAP2MandateVerifier.sol` - x402/AP2 payment mandate ("agent X may spend up to N per period for purpose
P") signed with Falcon-512 (PQC), verified via 0x0AE1. verifyMandate (view) + authorizeSpend (enforced:
window + cap + PQC). Settlement routes through the existing AERE402 rail (not redeployed); optional
SETTLEMENT_EXECUTOR gate.
Test: `aerenew/contracts/test/erc8004-adapters.test.js`
Doc: `aerenew/docs/AERE-ERC8004-X402-CONFORMANCE.md` (mapping table + PQC differentiator + honest status)
## REAL test result
`npx hardhat test test/erc8004-adapters.test.js` from aerenew/contracts -> **18 passing** (~2s).
Compiled 5 Solidity files (solc 0.8.23). Covers: identity register/resolve + controller gating; reputation
attest->read (forward into live AereAIReputation, score reads back) + honest not-attestor revert; validation
Falcon-512 (0x0AE1) accepted + ML-DSA-44 (0x0AE3) accepted + tampered rejected fail-closed + score-binding +
stale-precompile-invalid; mandate verify/authorize/cap/expiry/tampered-fail-closed/revoke-halt/executor-gate.
## Flags
- [MEASURE] Local Hardhat has no PQC precompile. Test installs the repo's MockPQCPrecompile at 0x0AE1/0x0AE3
via hardhat_setCode (same as the AereRecoveryRegistry fault-harness). Mock parses at the LIVE precompile
spec offsets (accept iff commitment == keccak256(pk||message)), so a pass proves correct input routing +
fail-closed. Real precompile acceptance is proven separately vs live mainnet 2800 via eth_call (as
AerePQCKeyRegistry/AerePQCAttestation are), NOT re-proven in this unit test. Adapters stay wired to
0x0AE1/0x0AE3 for production.
- [VERIFY] ERC-8004 exact selectors are evolving (identity trending to ERC-721 register/tokenURI;
reputation/validation arg orders unsettled). Adapters implement documented semantics; selector-level
conformance reconcile pending. Marked in NatSpec + doc.
- [VERIFY] x402/AP2 mandate field set still evolving (AP2 Intent vs Cart; x402 HTTP-402 payload). Struct
captures the common "spend up to N per period for purpose Q" authorization.
- [VERIFY] Reputation write-through needs Foundation to authorize this adapter as an attestor on mainnet
(immutable live set) - external/founder-gated. Read path works against the live instance today.
## Honesty boundaries respected
- Adapters wrap live contracts, do NOT redeploy them. Consensus is NOT made post-quantum (still Besu QBFT
classical ECDSA); only the validation-response and mandate AUTHENTICITY are PQC.
- Standard-adoption by the wider ecosystem is external, stated as not controlled by Aere.
- Brand "Aere Network"/"Aere" title case, ticker AERE uppercase; no em-dashes / "--" in doc prose.
## Live addresses used (chain 2800)
AereAgentDID 0xce641d7d7C10553D82b06B7C21d423550e7522C5 | AereAIReputation 0x781ef746c08760aa854cDa4621d54db6734bfeBF
AereAgentBond 0x32E0015F622a8719d1C380A87CE1a09bcd0cB86A | AerePQCKeyRegistry 0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691
AERE402FacilitatorV2 0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de | AereSink 0x69581B86A48161b067Ff4E01544780625B231676
Precompiles: 0x0AE1 Falcon-512, 0x0AE3 ML-DSA-44 (live, activation block 9,189,161)

View File

@ -0,0 +1,236 @@
# Aere Network: ERC-8004 and x402/AP2 Conformance
Status: adapters built and tested. The live deployed agent contracts are unchanged. Adoption of these
standards by the wider agent ecosystem is external to Aere and is not something Aere controls.
This document describes how Aere Network conforms to two emerging agent standards through THIN ADAPTERS
over its already deployed, live agent stack, and where Aere adds something no one else has: an on-chain
Validation registry and AP2 style mandates whose proofs are POST-QUANTUM signatures (Falcon-512,
ML-DSA-44), verified in full on-chain by Aere's live native precompiles.
- ERC-8004 ("Trustless Agents") standardizes on-chain agent Identity, Reputation, and Validation
registries on top of the A2A (Agent-to-Agent) protocol.
- x402 (Coinbase) and AP2 (Google, Visa, and partners) standardize agent payment authorizations and
mandates.
Conforming to these kills the isolation risk of running a private agent trust layer: an Aere agent
becomes discoverable, rateable, and payable by any tool that speaks these standards. Aere's differentiator
is that the trust and payment proofs can be quantum-durable, while everyone else's are ECDSA and therefore
quantum-forgeable.
Scope boundary (honest): these are APPLICATION LAYER adapters and verifiers. They do NOT make Aere
consensus post-quantum. Aere blocks are still produced and signed by Besu QBFT validators with classical
secp256k1 ECDSA. What is post-quantum here is the AUTHENTICITY of a validation response and of a payment
mandate, verified by the live precompiles.
## What was built
New directory `aerenew/contracts/contracts/erc8004/`:
| File | Role |
| --- | --- |
| `IERC8004.sol` | The modeled ERC-8004 Identity, Reputation, and Validation registry interfaces. |
| `AereIdentityRegistry8004.sol` | ERC-8004 Identity adapter over the deployed AereAgentDID. |
| `AereReputationRegistry8004.sol` | ERC-8004 Reputation adapter over the deployed AereAIReputation. |
| `AereValidationRegistry8004.sol` | ERC-8004 Validation registry with POST-QUANTUM proofs (the differentiator). |
| `AereAP2MandateVerifier.sol` | x402/AP2 payment mandate verifier with a Falcon-512 (PQC) mandate signature. |
Test: `aerenew/contracts/test/erc8004-adapters.test.js` (18 passing, see "Test result" below).
The adapters take the live contract addresses in their constructors and wrap them. They never redeploy
or modify the underlying deployed primitives.
## Live deployed primitives being adapted (Aere mainnet, chain 2800)
| Primitive | Address | Notes |
| --- | --- | --- |
| AereAgentDID | `0xce641d7d7C10553D82b06B7C21d423550e7522C5` | Falcon rooted agent identity; agent id is the Falcon root key id. |
| AereAIReputation | `0x781ef746c08760aa854cDa4621d54db6734bfeBF` | Composable reputation keyed by (operator, agentId); attestor set fixed at deploy (Foundation). |
| AereAgentBond | `0x32E0015F622a8719d1C380A87CE1a09bcd0cB86A` | Slashable stake feeding the reputation score. |
| AerePQCKeyRegistry | `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691` | PQC public key registry; `verifyWithKey` routes to the live precompile for the key's scheme. |
| AERE402FacilitatorV2 | `0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de` | HTTP 402 settlement rail (classical hot session signature), 25 bps fee to AereSink. |
| AERE402FacilitatorPQC | READY, not yet deployed | Falcon rooted settlement rail (built and tested). |
| AereSink | `0x69581B86A48161b067Ff4E01544780625B231676` | Immutable 3 bucket fee router. |
Live native precompiles (activated on mainnet 2800 at block 9,189,161):
| Scheme | Precompile | Standard |
| --- | --- | --- |
| Falcon-512 | `0x0AE1` | NIST FIPS 206 draft line |
| ML-DSA-44 | `0x0AE3` | NIST FIPS 204 (Dilithium2) |
## ERC-8004 mapping table
| ERC-8004 requirement | Aere adapter (function) | Underlying live contract (call) |
| --- | --- | --- |
| Identity: register an agent with a DID and metadata URI | `AereIdentityRegistry8004.register(agentId, agentDomain, agentCardURI)` | AereAgentDID.agentExists + AerePQCKeyRegistry.ownerOf (controller gate); metadata stored in the adapter (the live DID carries no AgentCard URI). |
| Identity: resolve an agent | `AereIdentityRegistry8004.getAgent / resolveByDomain / resolveByAddress` | AereAgentDID (agent existence) + AerePQCKeyRegistry.ownerOf (current controller as agentAddress). DID string `did:aere:<chainId>:<agentId>`. |
| Reputation: attest feedback | `AereReputationRegistry8004.giveFeedback(agentId, delta, evidenceHash, uri)` | AereAIReputation.attest(operator, bytes32(agentId), delta, evidenceHash, uri). |
| Reputation: read feedback | `AereReputationRegistry8004.getScore / getStats` | AereAIReputation.scoreOf / statsOf (read straight from the deployed contract). |
| Validation: request | `AereValidationRegistry8004.requestValidation(validatorKeyId, serverAgentId, dataHash)` | AerePQCKeyRegistry (validator key ACTIVE + Falcon-512/ML-DSA-44) + AereAgentDID.agentExists (server agent). |
| Validation: respond with a proof | `AereValidationRegistry8004.respondValidation(requestId, response, pqcProof)` | AerePQCKeyRegistry.verifyWithKey -> live precompile 0x0AE1 / 0x0AE3 (full on-chain PQC verification, fail-closed). |
### Identity mapping detail
The ERC-8004 agent id IS the AereAgentDID agent id, which is the Falcon ROOT key id in AerePQCKeyRegistry
(`agentId == rootKeyId`). So an ERC-8004 resolve is a direct pass-through to the live DID: the agent's
on-chain root of authority is a quantum-durable Falcon key, not a bare secp256k1 address. `register` does
not mint a new identity (it already exists in the live DID). It binds an ERC-8004 AgentCard URI and domain
to an existing DID agent, gated to that agent's current controller (the registry owner of the Falcon root
key), so only the party that proved Falcon possession can publish the agent's ERC-8004 metadata.
`[VERIFY: confirm against the finalized ERC-8004 interface]` The newer ERC-8004 Identity draft leans toward
an ERC-721 registration (`register(tokenURI)` minting a sequential id). Aere anchors the ERC-8004 identity
to the pre-existing Falcon rooted DID id instead, because the trust root is the on-chain Falcon key rather
than a token. The resolve, getAgent, and AgentCard URI semantics match ERC-8004; the id minting selector
differs by design and is documented in the contract NatSpec.
### Reputation mapping detail
The live reputation is keyed by (operator address, bytes32 agentId). The adapter derives operator as the
agent's current controller (resolved from the Identity adapter) and agentKey as `bytes32(agentId)`, so an
ERC-8004 read or write addresses exactly one live reputation slot, deterministically.
Honest write constraint: AereAIReputation's attestor set is fixed at its deploy and has no add path. For
`giveFeedback` to land in the live score, the adapter must be a registered attestor on the reputation
instance it is constructed with. The currently live AereAIReputation lists the Foundation attestor only
(immutable), so pairing this adapter for write-through is a Foundation governance decision, external to this
contract and founder gated. `[VERIFY: on mainnet, register this adapter as an attestor before relying on
giveFeedback, or have the Foundation attestor call the live attest directly.]` The READ path needs no such
authorization and adapts the currently deployed instance as is. The adapter surfaces this with
`canWriteThrough()` and a clear `AdapterNotAttestor` revert, and the test proves both the authorized
write-through path and the unauthorized honest revert.
`[VERIFY: confirm against the finalized ERC-8004 interface]` Later ERC-8004 reputation drafts carry a richer
feedback payload (score, tag, response URI). Here the feedback signal is normalized to the live model's
delta in {-1, 0, +1} so no information is invented beyond what the deployed contract records.
## The post-quantum differentiator: Validation registry
`AereValidationRegistry8004` is the piece no other agent stack offers. It exposes the ERC-8004 Validation
surface (request and respond with a proof), where the validator's RESPONSE proof is a POST-QUANTUM signature
(Falcon-512 or ML-DSA-44), verified in full ON-CHAIN by Aere's live native precompiles (0x0AE1, 0x0AE3)
through the deployed AerePQCKeyRegistry.
Flow:
1. `requestValidation(validatorKeyId, serverAgentId, dataHash)`. Records a request that the PQC validator
identified by `validatorKeyId` validate the work the server agent committed to via `dataHash`. The
validator key must be an ACTIVE Falcon-512 or ML-DSA-44 key in the registry; the server agent must exist
in the live AereAgentDID.
2. `respondValidation(requestId, response, pqcProof)`. The validator answers with a score (0..100, the
ERC-8004 convention) and a PQC signature over the contract's domain separated challenge, which binds
(requestId, dataHash, serverAgentId, validatorKeyId, response). The signature is verified on-chain via
the live precompile. On success the response is recorded once. On any invalid or tampered proof the call
REVERTS and records nothing (FAIL-CLOSED). An empty or zero precompile return (a stale, un-activated
chain) is treated as INVALID.
Non-replayable and non-forgeable: the challenge binds chainId, this contract, the exact request tuple, and
the response, so a proof for one (request, response) can never be reused for another, another contract, or
another chain. A request completes exactly once. Authorship is the PQC signature alone: respond may be
relayed by any sender, since only the validator private key can produce a valid signature over the contract
bound challenge.
Why it matters: every other agent validation stack authenticates validator responses with ECDSA, which a
quantum computer forges. This one does not. It is the on-chain, post-quantum answer to "prove an independent
validator actually signed off on this agent's output".
## x402 / AP2 post-quantum mandate
`AereAP2MandateVerifier` is a minimal verifier for AP2 style payment MANDATES whose authorizing signature is
POST-QUANTUM (Falcon-512), verified in full on-chain by the live precompile (0x0AE1) through the deployed
AerePQCKeyRegistry. Settlement of the authorized spend routes through the EXISTING AERE402 rail
(AERE402FacilitatorPQC or AERE402FacilitatorV2), which is not redeployed or modified.
The struct:
```solidity
struct Mandate {
uint256 agentKeyId; // the paying agent's Falcon-512 key in AerePQCKeyRegistry
address token; // the settlement token the cap is denominated in
uint256 maxAmount; // cumulative spend cap over the window
uint64 periodStart; // unix seconds, inclusive
uint64 periodEnd; // unix seconds, inclusive
bytes32 purpose; // opaque purpose tag, e.g. keccak256("inference:anthropic")
uint256 mandateNonce; // per-agent mandate index (lets an agent hold several mandates)
}
```
This encodes the common AP2/x402 authorization: "agent X may spend up to N of token T per period P for
purpose Q". `mandateHash(m)` binds chainId and the verifier address, so a signed mandate cannot be replayed
against another verifier or chain.
The verify path:
- `verifyMandate(m, falconSig)`: a pure on-chain PQC check that the agent's Falcon key signed the mandate
(view, permissionless, free via eth_call).
- `authorizeSpend(m, amount, falconSig)`: the enforced path. Verifies the Falcon mandate signature via the
live precompile, checks the validity window and the cumulative cap, records the spend against the mandate,
and emits `MandateAuthorized`. Fail-closed on any invalid or tampered signature, an out-of-window mandate,
or a cap overflow. Rotating or revoking the agent's Falcon key halts the mandate immediately.
Settlement routing: `authorizeSpend` records that `amount` is authorized under the mandate; the actual token
movement plus the 25 bps fee to AereSink is performed by the live AERE402 rail. To keep the cumulative cap
honest (only real settlements should consume it), `authorizeSpend` is gated to an immutable
`SETTLEMENT_EXECUTOR` when one is configured (the AERE402 facilitator, which calls it atomically with
settlement). When `SETTLEMENT_EXECUTOR` is the zero address the accounting path is open (for verification and
testing); the pure PQC check `verifyMandate` is always open.
Why it matters: AP2 and x402 mandates today are ECDSA authorizations, quantum-forgeable. An Aere mandate is a
Falcon-512 authorization, quantum-durable, verified on-chain.
`[VERIFY: confirm against the finalized x402 / AP2 mandate schema]` The x402 and AP2 mandate field sets are
still evolving (AP2 distinguishes Intent versus Cart mandates; x402 carries an HTTP 402 payment payload).
This struct captures the common "spend up to N per period for purpose Q" authorization; reconcile the exact
field set and encoding with the final specs before claiming wire level conformance.
## Test result (real)
Command, run from `aerenew/contracts`:
```
npx hardhat test test/erc8004-adapters.test.js
```
Result: 18 passing.
- Identity: register an existing DID agent, resolve by id / domain / controller address, controller only
register and update, unknown agent and duplicate domain rejection.
- Reputation: forward feedback into the live AereAIReputation and read the score back (attest then read),
out-of-range delta and unregistered agent rejection, and the honest AdapterNotAttestor revert when the
adapter is not an authorized attestor (the immutable mainnet case), with the read path still working.
- Validation: a genuine Falcon-512 (0x0AE1) response accepted and recorded, a genuine ML-DSA-44 (0x0AE3)
response accepted, a tampered proof rejected fail-closed with nothing recorded, a proof bound to a
different response score rejected, a wiped precompile treated as invalid, and request guards
(inactive/non-PQC validator key, unknown server agent).
- Mandate: a genuine Falcon mandate verified (pure view) and spend authorized up to the cap, over-cap
rejected, expired window rejected, tampered signature rejected fail-closed, revoked Falcon key halts the
mandate, and the settlement-executor gate enforced.
`[MEASURE]` Local Hardhat has no PQC precompile, so the test installs the repo's `MockPQCPrecompile` at
0x0AE1 and 0x0AE3 via `hardhat_setCode`, exactly as the fault-harness `AereRecoveryRegistry` test does. The
mock parses the input at the LIVE precompile's spec offsets and accepts iff commitment equals
keccak256(pk || message), so a positive result proves the adapter routed the correct input to the correct
precompile and enforces fail-closed and non-replay semantics. The REAL precompile path (that the on-chain
0x0AE1 / 0x0AE3 accept a genuine Falcon-512 / ML-DSA-44 signature) is exercised separately against live
mainnet 2800 via eth_call, the same way AerePQCKeyRegistry and AerePQCAttestation are proven, and is not
re-proven inside this unit test. The adapters stay hard-wired to the live registry and precompiles for
production.
## Honest status summary
- The adapters are built and tested. They wrap the live deployed contracts and do NOT redeploy or modify
them.
- The Identity resolve path and the Reputation read path adapt the currently deployed contracts as is, with
no change and no authorization.
- The Reputation write-through path requires the adapter to be an authorized attestor on the reputation
instance; the live instance's attestor set is immutable Foundation-only, so this is an external, founder
gated governance decision.
- The Validation registry and the AP2 mandate verifier verify Falcon-512 and ML-DSA-44 signatures through the
live precompiles. This is application layer post-quantum authentication. It does NOT make Aere consensus
post-quantum.
- Selector level conformance with the final ERC-8004 and x402/AP2 texts is marked `[VERIFY]` wherever the
specs are still in flux; the adapters implement the documented semantics.
- Whether the wider agent ecosystem adopts these standards is external to Aere and is not something Aere
controls. What Aere controls is being ready and being the only stack whose agent validation and payment
mandates can be quantum-durable.

View File

@ -0,0 +1,123 @@
# Spec 3: Deterministic fault-injection harness + on-chain attested recovery
Built 2026-07-19. Purpose: turn "we ran some fault tests" into a reproducible adversarial evidence
suite that de-risks the biggest gated item (the post-quantum consensus flip) for zero capital.
Extends the existing measured fault-tolerance work into a repeatable chaos matrix plus an on-chain
recovery-attestation contract, honest about what runs locally vs what needs a live testnet.
**Scope boundary (held throughout):** this DE-RISKS the consensus flip. It does NOT activate
post-quantum consensus. That flip stays founder-gated and audit-gated.
---
## Part 1: `AereRecoveryRegistry.sol` (REAL, BUILT + TESTED)
- Contract: `aerenew/contracts/contracts/AereRecoveryRegistry.sol` (solc 0.8.23, repo style).
- Test: `aerenew/contracts/test/AereRecoveryRegistry.test.js`.
Records recovery events (validator-set drop below quorum -> halt -> subsequent resume), each
authenticated by a Falcon-512 signature verified on-chain by the LIVE precompile 0x0AE1.
- Fields per record: haltHeight, resumeHeight, faultKind (1=crash, 2=netsplit,
3=byzantine-proposer), attesting operator key (operatorId), Falcon signature over the record
hash, plus validator-set context (nValidators, quorum, aliveDuringFault) that encodes the
"drop below quorum."
- **Fail-closed:** invalid signature, stale nonce, inconsistent heights, or bad validator set
records nothing and advances no nonce; an empty/zero/short precompile return (pre-fork / stale
chain) is treated as INVALID. Reverts with `PQCVerificationFailed`.
- **Append-only:** records live in a push-only array; recordId is an immutable index; a strictly
increasing per-operator nonce is consumed each write (no replay). There is NO owner, admin, or
overwrite/delete function anywhere (structurally verified in the test's ABI scan).
- Wired to 0x0AE1 for production; Falcon-512 encoding matches the live precompile
(pk 897/header 0x09; envelope nonce(40) || esig(0x29..)), mirroring `AerePQCAttestation.sol`.
### REAL TEST RESULT: 10/10 PASSING
Command: `npx hardhat test test/AereRecoveryRegistry.test.js` from `aerenew/contracts`.
Compiled clean; all 10 tests green (~1s):
- registerOperator: accepts well-formed Falcon-512 key + sequential ids; rejects wrong length/header.
- valid attestation records: kill-2 (f=2 boundary) crash recovery stores every field; netsplit +
byzantine-proposer under one operator with nonce advancing.
- fail-closed: tampered signature rejected (nothing recorded); signature bound to different
heights/fault-kind rejected; empty precompile return treated invalid; unknown operator / invalid
fault kind / bad heights / bad validator set all revert.
- append-only: appends without rewriting an earlier record; replay of a consumed signature rejected;
ABI exposes no set/update/delete/overwrite/admin/owner function.
**[MEASURE] flag on Part 1:** the Hardhat EVM has no 0x0AE1, so the test installs the repo's
faithful `MockPQCPrecompile` (scheme 1) at 0x0AE1 via `hardhat_setCode` (same pattern the existing
`AerePQCAttestation.test.js` uses). The mock parses input at the LIVE precompile's spec offsets, so
a passing record proves the contract builds the 0x0AE1 input correctly. The real-precompile path
(that on-chain 0x0AE1 accepts a genuine Falcon-512 signature) is proven separately against live
mainnet 2800 via eth_call, not inside the unit test.
---
## Part 2: chaos-matrix runner (SCAFFOLD + CONFIG; multi-node runs [MEASURE])
Under `aerenew/chaos/`:
- `matrix.json` — single source of truth. N=7 (quorum 5, f=2) and N=9 (quorum 6, crash-tol 3)
fault matrix: C1 kill-1, C2 kill-2 (N=7 f=2 boundary), C3 kill-3 (N=7 sub-quorum halt / N=9
crash boundary), C4 kill-4 (N=9 sub-quorum halt), P1 network partition (minority halts), B1
byzantine-proposer. Documented tool = Pumba (toxiproxy / Chaos Mesh as alternatives), plus a
process-mode fallback that reuses the existing `audit-package-pq-consensus/harness/n7/` scripts.
- `run-chaos-matrix.sh` — reads the matrix, injects each fault, snapshots per-node heights across
the fault + recovery windows, decides halt/resume/fork, and (on a halt-then-recover) invokes the
attester. Has a hard guard that REFUSES to run on a production host.
- `lib/partition.sh` — network partition via pumba netem / iptables so the majority keeps quorum.
- `lib/attest-recovery.js` — ethers script that reads the record hash from the deployed registry,
signs it with the operator's offline Falcon-512 key, and submits `submitRecovery(...)`.
Validated locally: `matrix.json` parses (6 scenarios, N7/q5 + N9/q6); `bash -n` clean on both
shell scripts; `node --check` clean on the JS.
**[MEASURE] flag on Part 2:** the ACTUAL multi-node runs require a live isolated QBFT testnet on a
SCRATCH box, NEVER the infra box. The runner is inert without a net up (heights read 0). The
already-measured numbers are CITED from `matrix.json` `evidence`, not re-invented:
- Kill-2 soak (docs/FAULT-TOLERANCE-CHARACTERIZATION-2026-07-14.md): 100 adversarial cycles
(40 RT=4 mixed + 40 RT=1 mixed + 20 pure-Besu), **0 forks, 100/100 self-resync**, rare
temporary self-recovering f=2-boundary stall (3/40 RT=4, 1/40 RT=1, 0/20 Besu) that is inherent
QBFT-at-f=2 (FLP), not a client bug.
- Sub-quorum halt-and-recover and partition (spec 5.3/5.4): halts in place, no fork, resumes from
the exact halt height on restore, no re-genesis.
- Byzantine negatives (harness/n7/run-b-negatives.sh): 1-2 Falcon-faulty TOLERATED (<= f=2),
3 faulty (> f) HALT post-detection, restore RESUMES with no re-genesis.
---
## Part 3: watchtower design (DESIGN)
`aerenew/chaos/WATCHTOWER-DESIGN.md`. An independent off-chain observer holding a registered
Falcon-512 key that: (1) detects a halt by combining a progress stall with quorum loss and no
fork (so it does not false-positive on the rare temporary f=2-boundary stall), classifying the
fault kind; (2) waits for quorum restoration, doing nothing that could influence consensus; (3)
submits the signed recovery attestation to Part 1, verified fail-closed by 0x0AE1. Includes the
watchtower's own failure modes and the honest single-operator caveat.
Composition: Part 2 generates adversarial fault evidence reproducibly, Part 3 produces it live and
independently, Part 1 makes all of it un-doctorable and quantum-durable on chain. Together they
make the flip decision rest on honest, checkable evidence. The flip itself stays a founder + audit
decision; the suite does not pull the trigger.
---
## All [VERIFY] / [MEASURE] flags
- [MEASURE] Part 1: real-precompile 0x0AE1 acceptance of a genuine Falcon-512 sig is proven vs live
mainnet via eth_call, not in the Hardhat unit test (Hardhat has no 0x0AE1; mock installed there).
- [MEASURE] Part 2: multi-node chaos runs need a live isolated QBFT testnet on a scratch box; not
run here. Existing soak numbers cited, none invented.
- [VERIFY] Part 2/3: `lib/attest-recovery.js` needs the operator's offline Falcon-512 signer + key
(AERE_FALCON_SIGN / AERE_FALCON_KEY) and a chain where 0x0AE1 is live to complete the submit; on
a plain testnet without the precompile the on-chain verify fails closed (correct, safe).
- [MEASURE]/[VERIFY] Part 3: the live watchtower daemon and its detection-threshold tuning against
live inter-validator RTT are designed, not built; single-operator vantage is corroboration, not
independence, until third-party watchtowers run.
## Files
- aerenew/contracts/contracts/AereRecoveryRegistry.sol
- aerenew/contracts/test/AereRecoveryRegistry.test.js
- aerenew/chaos/matrix.json
- aerenew/chaos/run-chaos-matrix.sh
- aerenew/chaos/lib/partition.sh
- aerenew/chaos/lib/attest-recovery.js
- aerenew/chaos/WATCHTOWER-DESIGN.md
- aerenew/chaos/README.md

View File

@ -0,0 +1,115 @@
# Formal-verification broadening: new contract SMT models + canonical runner consolidation
Date: 2026-07-19. Tooling: Python 3.14.4, z3 4.16.0 (real z3 verdicts, no fabricated greens).
All verdicts below were produced by actually running the scripts.
## Part 1: four new z3 SMT models (all PROVED, negative controls fired)
Each model proves its safety property by asserting NOT-property under the real Solidity
guards (z3 returns UNSAT) and pairs every proof with a firing negative control that removes
one guard and returns SAT (non-vacuity). DESIGN-level caveats and [VERIFY]/[MEASURE] items
are stated in each model's SUMMARY block exactly as the money-contract models do.
### a. pqfinality_smt.py -> PROVED (10 PROVED, 3 CEX-FOUND, 0 FAILED, rc=0)
Contracts: contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol +
AerePQAttestationKeyRegistry.sol
Proved: quorum threshold is exactly ceil(2N/3) = (2N+2)/3 (Q0 for all N>=1; Q0b anchors
N in {4,5,7,9} -> {3,4,5,6}); an accepted certificate meets that quorum (P1; P1b a 4-of-7
cert is rejected); it is bound to the live registry validatorSetRoot (P2) and size (P2b); it
is bound to the chainId+registry attestation domain so a cross-chain/registry certificate is
rejected (P3 via injective domain hash; P3b direct); a key rotation that changes the root
makes an old-root certificate unaccept-able (P4 via injective root hash); an empty validator
set is rejected (P5).
Negative controls (all fired SAT): drop quorum -> sub-quorum accepted; drop root binding ->
stale/forged root accepted; drop domain binding -> cross-chain replay accepted.
Caveats stated: SP1 gateway soundness (verifyProof reverts on invalid proof) is a trusted
primitive [VERIFY]; the off-chain zkVM aggregation circuit is [MEASURE], NOT implemented
(contract tests use a MOCK gateway); keccak256 injectivity (root/domain) is [VERIFY];
additive PQ finality, consensus stays classical ECDSA QBFT; DESIGN math not EVM bytecode.
### b. pqaggregate_smt.py -> PROVED (7 PROVED, 2 CEX-FOUND, 0 FAILED, rc=0)
Contract: contracts/contracts/mpc/AerePQAggregateVerifier.sol
Proved: a registered committee is well-formed (R0: root!=0, 1<=t<=n, scheme!=0); an accepted
authorization is EXACTLY the registered committee's key set (P1; P1b via committee-root
injectivity, so a foreign key set is rejected); it is bound to the caller's message digest
(P2); it proves at least threshold t distinct signatures (P3); it binds size and scheme as
defense in depth (P4); and the on-chain accept reads only O(1) committee/public fields (keys
not stored), so verification cost is independent of n (P5).
Negative controls (all fired SAT): drop root binding -> a different committee's key set
authorizes; drop digest binding -> a proof for message A authorizes message B.
Caveats stated: SP1 gateway soundness [VERIFY]; off-chain zkVM aggregation circuit [MEASURE],
not implemented (MOCK gateway); keccak256 injectivity [VERIFY]; account/authorization
aggregation not consensus, no funds, no admin; DESIGN math not EVM bytecode.
### c. credential_registry_smt.py -> PROVED (7 PROVED, 3 CEX-FOUND, 0 FAILED, rc=0)
Contracts: contracts/contracts/compliance/AereTrustRegistry.sol + AereVerifiableCredential.sol
Proved: issuance requires accreditation (A: anchor success => Active AND in-scope; A2: a
None/Suspended/Revoked or out-of-scope issuer cannot anchor) and a valid Falcon-512 issuer
signature (A3); the status history is append-only so a committed record cannot be rewritten
(B, via the standard append relation); revocation is terminal (C: from Revoked no transition
returns to Active); and both the anchored (isValid, D) and presented (verifyPresented, D2)
verification paths are fail-closed on expired / not-yet-valid / revoked / un-accredited.
Negative controls (all fired SAT): drop the accreditation check -> a revoked issuer issues;
allow a status overwrite -> a committed record is silently rewritten; drop the validity
window -> an expired credential is accepted.
Caveats stated: Falcon-512 precompile 0x0AE1 soundness modelled as the sigValid predicate
[VERIFY]; the Bitstring Status List and live issuer status are trusted external oracles;
application-layer, no funds, consensus stays classical ECDSA QBFT; legal weight of any
accreditation is external; DESIGN logic not EVM bytecode.
### d. recovery_registry_smt.py -> PROVED (5 PROVED, 2 CEX-FOUND, 0 FAILED, rc=0)
Contract: contracts/contracts/AereRecoveryRegistry.sol
Proved: a record is written only if the Falcon-512 signature verifies (FC, fail-closed) and
is well-formed (FC2: fault kind / heights / validator set); the per-operator nonce strictly
increases on every write (NO); a nonce-k signature cannot be replayed once the nonce advances
because the record hash binds the nonce (RP, via injective nonce hash + EUF-CMA verify
predicate); and the records array is append-only so a committed record is never rewritten (AP).
Negative controls (all fired SAT): drop the nonce binding -> one signature replays to rewrite
history; drop append-only -> a committed record is silently overwritten.
Caveats stated: Falcon-512 precompile 0x0AE1 soundness (EUF-CMA, input parsing,
empty-return-is-invalid) modelled as the verify predicate [VERIFY]; keccak256 injectivity of
the nonce-bound record hash [VERIFY]; records attestations only, does not observe consensus or
activate PQ consensus (still classical ECDSA QBFT); uint64 nonce cannot realistically
overflow; DESIGN logic not EVM bytecode.
## Part 2: canonical runner consolidation
File changed: aerenew/formal-consensus/run_consensus_verification.py (had a hardcoded MODELS
list of 8 consensus models). Added a CONTRACT_MODELS section wiring in all 7 new contract SMT
scripts written in this loop, keeping the 8 consensus models intact:
computemarket_smt, destinationsettler_smt, migrator_smt (the 3 money models) +
pqfinality_smt, pqaggregate_smt, credential_registry_smt, recovery_registry_smt (the 4 new).
The runner now also captures each child's output and tallies the per-check verdict lines.
### Canonical runner fresh totals (real run, exit 0)
models run: 15 passed: 15 (8 consensus + 7 contract)
verdict lines: 128 PROVED 79 CEX-FOUND 0 FAILED
RESULT: ALL MODELS PASS (proofs PROVED, negative controls fired)
Breakdown of the 128/79 (per-check verdict lines, i.e. distinct property checks):
- 4 new models: 29 PROVED / 10 CEX-FOUND (pqfinality 10/3, pqaggregate 7/2,
credential_registry 7/3, recovery_registry 5/2)
- 3 fund-flow models: 26 PROVED / 11 CEX-FOUND (computemarket 16/3, destinationsettler 5/4,
migrator 5/4)
- 8 consensus models: 73 PROVED / 58 CEX-FOUND (remainder)
Note on scope: the 7 wired-in contract models live in aerenew/formal-consensus/ (the same
directory as the canonical runner). The research-stage staging copy
(aerenew/research-stage/formal-consensus/) is a separate curated publish target with its own
committed expected-output.log and was left unchanged.
### REPRODUCE.md update
Updated aerenew/REPRODUCE.md section 3b: the "canonical in-tree" subsection previously cited
"8 models" / "8 of 8 models PASS". It now reads "15 models: 8 consensus + 7 contract", "15 of
15 models PASS", with the real fresh runner tally of 128 PROVED / 79 CEX-FOUND / 0 FAILED, the
per-group breakdown above, the names of the 7 newly-added contract models, and a restatement
of the NOT-property-UNSAT + firing-negative-control discipline and the per-model DESIGN /
[VERIFY] / [MEASURE] boundaries. No fabricated numbers: every figure is from the actual run.
The staging "(10 models)" section and the older bash-loop "(9 models)" contract section were
left as-is (correct and separate).
## Honesty posture
- All verdicts are real z3 output; no green was fabricated.
- Every model has at least one firing negative control (SAT under a removed guard).
- No contract was modified. Only SMT scripts were added, the runner was extended, and
REPRODUCE.md was updated with real fresh numbers.

View File

@ -0,0 +1,191 @@
# Formal-verification extension: new fund-flow contracts (z3 SMT + full regression)
Date: 2026-07-19
Scope: three new money contracts for Aere Network, modelled in z3 in the exact style of the
existing consensus/contract SMT corpus, plus a full regression of the new-contract test suite to
confirm the recent security fixes (M1 giveFeedback gate, L1 codeless-gateway guards, L2 VectorStore
receipt binding) broke nothing.
Honesty posture (same caveat as the rest of the corpus): each SMT script checks the DESIGN-level
accounting/guard logic over the modelled transitions with unbounded Ints, NOT the compiled EVM
bytecode. A model counts as non-vacuous only because a NEGATIVE CONTROL (one guard removed) fires a
real counterexample. Trusted external primitives (SP1 gateway proof soundness, the Falcon-512
precompile at 0x0AE1, SafeERC20 whole-tx revert) are marked [VERIFY] and are not re-proved here.
All verdicts below are the REAL z3 output and the REAL mocha output, reproduced verbatim from the runs.
---
## PART 1: new z3 SMT models
All three scripts live in `aerenew/formal-consensus/` (they join the existing corpus next to
`spokepool_smt.py`, `settlementhub_smt.py`, `threshold_account_smt.py`). Run each with
`python <script>.py` (z3-solver 4.16.0, confirmed installed). Each exits 0 only if every PROOF is
PROVED and every NEGATIVE CONTROL fired.
### A. computemarket_smt.py -> AereComputeMarketV3, ESCROW SOLVENCY
Contract: `aerenew/contracts/contracts/depin/AereComputeMarketV3.sol`
Invariant (the contract's own `isSolvent`): per asset T, `bal[T] >= totalLiabilities[T]` and
`totalLiabilities[T] >= 0`, where a reward is escrowed in its own `token` and EVERY provider/challenger
bond is native AERE (address(0)). So native liabilities carry native-token rewards PLUS all bonds; an
ERC-20 asset carries only its own rewards. No fee, no owner, no sweep in this contract, so each op's
balance delta equals its liability delta on the same asset.
VERDICT: PROVED (16 proofs UNSAT), 3 NEGATIVE CONTROLS fired (SAT). Script exit 0.
Proofs (all z3=unsat, inductive across the real transitions):
- base case (empty contract) satisfies INV
- postJob(native) / postJob(ERC20, received==reward guard) preserve INV
- claimJob (bond escrowed to native), cancelJob (refund + uncommit) preserve INV
- submitResult (status-only, no fund move) preserves INV
- settle (acceptResult / finalize / submitResultZK): reward leg on token asset, bond-return leg on
native, AND the combined leg when token==native, all preserve INV
- reclaimExpired (reward + bond leave together) preserves INV
- dispute (challenger bond escrowed) preserves INV
- resolveDispute BOTH branches (providerWon / not) preserve INV on token and native legs
- no double-count: an ERC20-reward + native-bond settle keeps BOTH assets solvent, reward touched
only on its asset, bond only on native
- double-pay: a second payout after the terminal status is set is guard-infeasible
- ZK_VERIFIED: paying without a valid proof is impossible (settlement sits strictly behind the
`ZK_VERIFIER.verifyProof` gate, which reverts on an invalid proof)
Negative controls (all z3=sat, load-bearing):
- BUGGY postJob (commit full reward but receive less, i.e. no received==reward check) CAN under-back
the escrow [witness: reward=1, received=0]
- BUGGY claim (native bond mis-posted to the ERC-20 reward asset) CAN under-back that asset
[witness: bond=1, balE=0, liabE=0]
- BUGGY ZK settle (no verifyProof gate) CAN pay without a valid proof [witness: pay_zk=1, proofValid=0]
[VERIFY] SP1 gateway proof soundness and the Falcon-512 precompile at 0x0AE1 are trusted primitives,
not re-proved by this SMT model.
### B. destinationsettler_smt.py -> AereDestinationSettler, NO-ARBITRARY-RECIPIENT + ATOMICITY
Contract: `aerenew/contracts/contracts/intents/AereDestinationSettler.sol`
Guards modelled from the real `fill()`: G1 declOrderId==orderId, G2 declRecipient!=0, G3
declOutputToken!=0, G4 declOutputAmount!=0, G5 fills[orderId].filler==0 (unfilled), G6 delivered token
== declared token AND delivered recipient == declared recipient AND delivered amount >= declared amount.
VERDICT: PROVED (5 proofs UNSAT), 4 NEGATIVE CONTROLS fired (SAT). Script exit 0.
Proofs:
- P1 a successful fill can deliver ONLY to the order's declared recipient (an attacker address is
impossible)
- P1b the transfer target equals the declared recipient AND is non-zero
- P2 a second fill of an already-filled order is guard-infeasible (AlreadyFilled), so a solver is
repaid at most once per intent
- P2b the fill is bound to the arg orderId (G1), so no cross-slot record can dodge the duplicate check
- P3 an output that mismatches the declared token OR is short is rejected (OutputMismatch)
Negative controls:
- no-recipient-check settler CAN deliver to an arbitrary attacker [witness: declRecipient=1,
attacker=2, deliveredRecipient=2]
- no-AlreadyFilled settler CAN double-fill one order (double repayment)
- no-ZeroRecipient settler CAN record a delivery to address(0)
- no-OutputMismatch settler CAN record a wrong-token delivery [witness: declOutputToken=1,
deliveredToken=2]
[VERIFY] Delivery ATOMICITY (effects-before-interaction record, then a single SafeERC20 pull
solver->recipient that reverts the whole tx on failure, under nonReentrant) is an EVM-revert property
asserted structurally, not by this arithmetic model. [MEASURE] Origin-chain repayment is gated by the
validator-set-anchored finality+inclusion proof and is out of scope here.
### C. migrator_smt.py -> AereAccountMigrator, ONLY-DESTINATION + ATOMICITY
Contract: `aerenew/contracts/contracts/pqc/AereAccountMigrator.sol`
Guards modelled from the real `_migrate()`: H1 destination!=0, H2 length match, H3 each token/amount
!=0, H4 moveNative => msg.value!=0, H5 !moveNative => msg.value==0 (StrayNative, the no-strand guard),
H6 every ERC-20 leg is safeTransferFrom(caller, destination, amount), H7 native leg forwards the full
msg.value to destination and reverts on failure.
VERDICT: PROVED (5 proofs UNSAT), 4 NEGATIVE CONTROLS fired (SAT). Script exit 0.
Proofs:
- P1 every ERC-20 leg delivers ONLY to the caller's non-zero `destination` (no third-party target)
- P2 no custody: sum(pulled from caller) == sum(delivered to destination), migrator ERC-20 delta == 0
- P3 atomicity: no partial move, all legs commit or none (whole-tx revert)
- P4 no stranded native: migrator native delta == 0 (StrayNative guard + full forward)
- P5 migrateToPqcAccount honors a pinned expectedDestination (AddressMismatch guard)
Negative controls:
- BUGGY skim (a fee f>0 diverted to a third party) CAN leak funds off `destination` [witness:
amount0=2, fee=1, destGets=1, feeCollectorGets=1]
- BUGGY per-leg try/catch (continue past a failed leg instead of whole-tx revert) CAN leave a PARTIAL
move, i.e. a half-drained old account [witness: leg_ok_0=true, leg_ok_1=false]
- BUGGY no-StrayNative migrator CAN strand native value unrecoverably (no sweep exists) [witness:
msgValue=1]
- BUGGY no-ZeroDestination migrator CAN sweep to address(0) (burn) [witness: destination=0]
[VERIFY] Whole-tx atomicity rests on SafeERC20 reverting on a false/failing ERC-20 and on
nonReentrant; P3 models its consequence (all-or-nothing) rather than the opcode semantics.
### Part 1 summary table
| Model | File | Proofs (UNSAT) | Neg-controls (SAT) | Control fired? | Exit |
|-------|------|----------------|--------------------|----------------|------|
| AereComputeMarketV3 escrow solvency | computemarket_smt.py | 16 / 16 PROVED | 3 / 3 | YES | 0 |
| AereDestinationSettler no-arb-recipient + atomicity | destinationsettler_smt.py | 5 / 5 PROVED | 4 / 4 | YES | 0 |
| AereAccountMigrator only-destination + atomicity | migrator_smt.py | 5 / 5 PROVED | 4 / 4 | YES | 0 |
Every model is non-vacuous: each has at least one firing negative control. No z3 timeouts, no FAILED
lines. The out-of-decidable-scope pieces (EVM whole-tx revert atomicity, SP1/Falcon primitive
soundness, origin-chain repayment) are marked [VERIFY]/[MEASURE] honestly and are not claimed as proved.
---
## PART 2: full regression of the new-contract test suite
`npx hardhat compile` -> GREEN ("Nothing to compile", cache valid, exit 0).
Per-file `npx hardhat test test/<file>.test.js` (RandomnessBeacon run with
`--config hardhat.config.prague.js` so the EIP-2537 BLS12-381 pairing path actually executes):
| # | Test file | Result | Exit |
|---|-----------|--------|------|
| 1 | AereRecoveryRegistry.test.js | 10 passing | 0 |
| 2 | erc8004-adapters.test.js | 19 passing | 0 |
| 3 | AerePQAggregate.test.js | 18 passing | 0 |
| 4 | aere-pq-screen.test.js | 13 passing | 0 |
| 5 | destination-settler.test.js | 4 passing | 0 |
| 6 | AereRandomnessBeaconV2.test.js (prague config) | 15 passing, 1 pending | 0 |
| 7 | AereComputeMarketV3.test.js | 14 passing | 0 |
| 8 | aere-vector-store.test.js | 8 passing | 0 |
| 9 | account-migrator.test.js | 9 passing | 0 |
| 10 | AerePQFinalityCertificate.test.js | 12 passing | 0 |
GRAND TOTAL: 122 passing, 0 failing, 1 pending (all 10 files exit 0).
Notes:
- 0 failing across all 10 logs (verified by a grep for "failing": NONE).
- The 1 pending is intentional and honest: under the prague config EIP-2537 IS present, so the
"without EIP-2537, a genuine sig is also rejected" fail-closed-ABSENCE check is not applicable and is
skipped. The 15 passing include the real on-chain BLS12-381 pairing verification of genuine drand
quicknet vectors.
- destination-settler.test.js legitimately has 4 tests (valid fill, duplicate reverts, output mismatch
reverts, zero recipient reverts), the complete run, matching the properties modelled in Part 1.
The recent security fixes (M1 giveFeedback gate in the ERC-8004 adapters, L1 codeless-gateway guards,
L2 VectorStore receipt binding) introduced NO regression: erc8004-adapters (19), aere-vector-store (8),
and the whole suite stay green.
---
## Flags / honest caveats
- SMT models check DESIGN-level math/guards over the modelled transitions with unbounded Ints, NOT the
compiled EVM bytecode. Same boundary as the existing corpus.
- [VERIFY] items not proved by SMT: SP1 gateway proof soundness, Falcon-512 precompile (0x0AE1)
soundness, SafeERC20 whole-tx revert / nonReentrant atomicity.
- [MEASURE] AereDestinationSettler origin-chain repayment (off-chain SP1 prover + second chain) is out
of scope for both the SMT model and the unit test.
- 1 pending test in the regression is a deliberately-skipped not-applicable fail-closed-absence check
under the prague EVM, not a masked failure.
- No contract was modified. Only the three new SMT scripts were added.
```
Files added:
aerenew/formal-consensus/computemarket_smt.py
aerenew/formal-consensus/destinationsettler_smt.py
aerenew/formal-consensus/migrator_smt.py
```

View File

@ -0,0 +1,130 @@
# spec-formal-final: z3 SMT coverage for the new Aere Network contract suite
Prepared 2026-07-19. Environment: Python 3.14.4, z3-solver 4.16.0, Windows 11.
All four new models follow the corpus discipline exactly: each safety property is proved by asserting
NOT-property under the real Solidity guards (z3 returns UNSAT), paired with a firing negative control
that removes one guard and returns SAT (non-vacuity). Real verdicts only. No contract was modified.
## PART 1: new z3 SMT models (4 added, all PROVED, all negative controls fired)
### a. ap2_mandate_smt.py (AereAP2MandateVerifier) -- 9 PROVED / 3 CEX-FOUND, exit 0
Load-bearing invariant modelled: CAP-SOLVENCY, `INV(mh) := 0 <= spentUnder[mh] <= maxAmount`.
Proved (UNSAT under the real guards):
- base case (fresh mandate spent == 0) satisfies INV;
- authorizeSpend preserves INV inductively (cumulative spend stays within the cap);
- two sequential guarded draws can never exceed the cap (the requested windowed-cumulative property);
- authorizeSpend is monotone (consumed cap is never refunded / reduced, so a replay cannot free cap);
- an expired mandate authorizes nothing (write requires now <= periodEnd);
- a not-yet-valid mandate authorizes nothing (write requires now >= periodStart);
- an accepted authorization has a well-formed window (periodEnd >= periodStart);
- L3 executor gate (post-fix): with SETTLEMENT_EXECUTOR configured, only that executor can consume
the cap (the open replay path is blocked);
- a zero-amount draw is rejected.
Negative controls (SAT, all fired): drop the cap check -> authorizes over-cap; drop the expiry check
-> authorizes an expired mandate; drop the L3 executor gate -> a non-executor drains a configured
mandate's cap. All three requested controls present (cap, expiry, plus the L3 gate control).
Caveat / [VERIFY]: the Falcon-512 validity bit is the live precompile 0x0AE1 (trusted primitive, not
re-proved); mandateHash chain/contract binding and keccak256 injectivity are [VERIFY]; the contract
holds NO funds (cap is accounting, the AERE402 rail moves value); unbounded-Int design abstraction,
DESIGN-level not bytecode. The open path (executor == 0) is verification/test only per the contract's
documented L3; the model proves the configured executor closes the vector.
### b. reputation_gate_smt.py (AereReputationRegistry8004, M1 fix) -- 5 PROVED / 2 CEX-FOUND, exit 0
Invariant modelled: M1 FORWARD-SAFETY, a forwarded attestation happens => (author authorized) AND
(delta in {-1,0,+1}).
Proved (UNSAT): only an authorized feedback author can move a score (G0, the M1 gate); a forwarded
delta is clamped to {-1,0,+1} (G2, no arbitrary value set); the composite M1 property (a score move
requires an authorized author AND a clamped unit delta); feedback requires a registered agent (G1);
forwarding requires the adapter to be a live attestor (G3).
Negative controls (SAT, both fired): drop the author gate -> anyone drives an agent's score (this
reproduces the exact M1 bug); drop the delta clamp -> an arbitrary reputation delta is forwarded.
Honest caveat: this is the lightest of the four; its authorization half overlaps the Hardhat test
"gates giveFeedback to owner-authorized feedback authors (M1)". The z3 value-add is the composite
invariant plus the firing non-vacuity control, and the delta-clamp arithmetic bound. The live
AereAIReputation computes the score FROM the delta; the model bounds only what the adapter forwards
(authorized + clamped), not the live contract's score formula. Included because the task explicitly
requested it AND the clamp bound + firing M1 control make it non-vacuous, not coverage-theater.
### c1. vectorstore_smt.py (AereVectorStore) -- 8 PROVED / 3 CEX-FOUND, exit 0
Invariants modelled: VERSION MONOTONICITY, APPEND-ONLY history, retrieval-against-a-committed-version,
one-shot receipt.
Proved (UNSAT): a commit increments version by exactly one (version = s.version + 1); version strictly
increases; commitCount strictly increases; the version history is append-only (a committed root can
never be rewritten); every attestation names a committed version 1..current (CV); vector-inclusion is
only checked against a committed version (CV2); a priced receipt is consumed at most once (R1); only
the bound provider may consume a receipt (R2, the L2 fix).
Negative controls (SAT, all fired): drop the version bound -> attest against an uncommitted version;
drop append-only -> overwrite a committed root; drop the one-shot guard -> double-spend one paid
receipt.
Caveat / [VERIFY]: post-quantum commit AUTHENTICITY is the live precompile 0x0AE1 (fail-closed,
trusted); the AERE402 facilitator settlement in paidQuery is an external trusted rail (this model
covers the receipt STATE machine, not the token movement); Merkle inclusion rests on keccak256 / OZ
MerkleProof soundness. This satisfies the task's "check AereVectorStore version monotonicity" item.
### c2. bitstring_status_smt.py (AereBitstringStatusList) -- 6 PROVED / 2 CEX-FOUND, exit 0
Invariants modelled: REVOCATION-BIT MONOTONICITY (terminal), no silent no-op, epoch monotonicity,
bit-frame isolation.
Proved (UNSAT): on a Revocation list a status bit is monotone, the only enabled transition is 0 -> 1
(RM), so a revoked credential can never be un-revoked; an enabled revocation transition always starts
from an unset bit (RM2); every recorded mutation is a real change (NS); only the controller can write
(CTRL); the epoch strictly increases on every mutation (EP, the freshness anchor); setting one bit
leaves every other credential's bit unchanged (FR, frame isolation).
Negative controls (SAT, both fired): drop the terminal guard -> a Revocation list clears a bit
(un-revoke); drop the bit mask -> flip a neighbouring credential's bit while setting one.
Caveat: Suspension lists are intentionally reversible, so the monotonicity property is asserted only
for Revocation lists. The zk non-revocation path (verifyNonRevocation + SP1 circuit) is [MEASURE]
(circuit pending, fail-closed off until configured); its codeless-verifier L1 guard and fail-closed
binding are covered by the aere-pq-screen Hardhat tests, not modelled here. This satisfies the task's
"AereBitstringStatusList status-bit monotonicity of revocation" item.
## Contracts deliberately NOT modelled (honesty over coverage-theater)
Per the task's own guidance, these have key properties that are access-control / fail-closed /
precompile-return-handling, already covered by their Hardhat tests (and, where applicable, Halmos
symbolic). A z3 arithmetic model would be vacuous:
- AereIdentityRegistry8004: register is gated to the agent's current controller; resolve/getAgent are
pass-through reads. Pure access-control. Covered by test/erc8004-adapters.test.js.
- AereValidationRegistry8004: respondValidation is fail-closed PQC-return handling (reverts on an
invalid Falcon-512 / ML-DSA-44 proof) plus a one-shot Pending -> Completed status and a request
nonce used only for id derivation. Its structural invariants (fail-closed verify, record-once,
monotone nonce) are already the exact shapes proved by recovery_registry_smt and credential_
registry_smt, and it is covered by erc8004-adapters.test.js. Not forced.
- AereRandomnessBeaconV2: precompile-return-handling / fail-closed (task explicitly lists it).
- AerePQAggregateModule and the L1 fix on AerePQAggregateVerifier: the t-of-n authorization is already
covered by pqaggregate_smt.py; the L1 fix itself is a codeless-gateway constructor guard (fail-
closed), covered by the AerePQAggregate.test.js "rejects a code-less SP1 gateway at construction"
test. Not arithmetic.
- The other L1 codeless-verifier constructor guards (AereComputeMarketV3, AereBitstringStatusList):
fail-closed constructor guards covered by the AereComputeMarketV3 and aere-pq-screen Hardhat tests;
not arithmetic. (ComputeMarketV3's escrow arithmetic is already modelled by computemarket_smt.py;
BitstringStatusList's real arithmetic is the revocation monotonicity modelled above, not the L1
guard.)
Also, within the modelled contracts, the precompile-return / access-control halves (AP2 Falcon
verification, VectorStore L2 provider-binding) are noted as test/precompile-trust covered; the z3
models only the genuine arithmetic/state halves.
## PART 2: canonical runner new totals and REPRODUCE.md
All four new models wired into aerenew/formal-consensus/run_consensus_verification.py CONTRACT_MODELS
(existing 15 entries kept intact). Fresh canonical run on 2026-07-19:
models run: 19 passed: 19 (8 consensus + 11 contract)
verdict lines: 156 PROVED 89 CEX-FOUND 0 FAILED
RESULT: ALL MODELS PASS (proofs PROVED, negative controls fired) exit 0
Delta from the previous 15-model run (128 PROVED / 79 CEX-FOUND): +28 PROVED, +10 CEX-FOUND, exactly
the four new models' contribution (ap2_mandate 9/3, reputation_gate 5/2, vectorstore 8/3,
bitstring_status 6/2).
REPRODUCE.md section 3b updated to the real fresh count: "19 models: 8 consensus + 11 contract",
the tally "156 PROVED, 89 CEX-FOUND, 0 FAILED", the four newly-added models named with their
one-line invariants and per-model verdict-line contributions, and the [VERIFY] boundary note extended
to the ERC-8004 mandate / reputation adapters (the model bounds the adapter's guard logic and
accounting, not the AERE402 rail's token movement or the live reputation's score formula). No stale
"15 models" or "128 PROVED" counts remain; no em-dashes or "--" in the prose.
## Files added / changed
- NEW aerenew/formal-consensus/ap2_mandate_smt.py
- NEW aerenew/formal-consensus/reputation_gate_smt.py
- NEW aerenew/formal-consensus/vectorstore_smt.py
- NEW aerenew/formal-consensus/bitstring_status_smt.py
- EDIT aerenew/formal-consensus/run_consensus_verification.py (4 entries appended to CONTRACT_MODELS)
- EDIT aerenew/REPRODUCE.md (section 3b: 15 -> 19 models, 128/79 -> 156/89, named the new models)

103
AERE-MIGRATION-TOOLKIT.md Normal file
View File

@ -0,0 +1,103 @@
# spec-31: Aere Quantum Migration Toolkit
Status date: 2026-07-19
Location: `aerenew/pqc-migration-toolkit/`
Runtime: pure Node built-ins (Node 18+), zero runtime dependencies.
## What it is
A real, runnable toolkit that helps anyone assess and migrate an EVM account
toward post-quantum readiness on Aere Network (chain 2800) or any EVM chain via
RPC. It extends Aere's internal account-migration work outward into a usable
tool. Three parts plus a self-test and a README.
## The four deliverables
1. PQC-readiness scanner, `scan.js`. Given an EVM address, detects EOA vs
contract, and for contracts scans bytecode for quantum-vulnerable signature
primitives (ecrecover 0x01, P-256 0x100) versus Aere's live post-quantum
precompiles (0x0AE1..0x0AE5). Outputs RED (classical-only), YELLOW (hybrid),
GREEN (PQC-capable). Runnable: `node scan.js <address> --rpc <url>`, plus
offline `--bytecode 0x..` and `--fixture <name>` paths.
2. Migration SDK, `lib/migrate.js` + `plan.js`. CREATE2 address derivation
mirroring `AerePQCAccountFactory.predictAddress`, calldata builders for
`createAccount` (PQC account) and `registerIdentity` (hybrid ECDSA+Falcon),
and a step-by-step migration plan over Aere's existing live primitives
(AerePQCAccount/Factory, AereHybridAuth, AerePQCKeyRegistry, EIP-7702).
3. Migration cost simulator, `simulate.js`. Estimates migration gas from the
MEASURED PQC verify costs (Falcon-512 86,336 verify-and-record tx / 40,000
marginal; hybrid 89,336 / 43,000; Falcon-1024 145,496; ML-DSA-44 351,050;
SLH-DSA-128s 558,276), plus the CREATE2 deploy (1,493,084) and the deployed
Solidity-verifier path (~10.5M), at the 1 Gwei floor.
4. README, `README.md`. Honest positioning: Aere is the post-quantum EVM L1
(five live NIST PQC precompiles + a live Falcon-owned account and factory), so
it is the natural home for PQC migration tooling. This is a STARTING toolkit,
not a finished product.
## How the scanner is genuinely honest (not a grep)
- Opcode-aware decoding: walks the instruction stream skipping PUSH immediates,
so a matched constant is a real push (a naive `610ae1` substring search
false-positives).
- Local stack simulation: for every CALL/STATICCALL/DELEGATECALL/CALLCODE it
recovers the address argument (2nd stack item) per basic block, so it flags an
actual call to 0x0AE1 rather than the number 0x0AE5 used as a memory length.
An earlier proximity-only version misread AereHybridAuth as YELLOW for exactly
that reason; the stack-simulation version reads it RED correctly.
- Signature-verify addresses 0x0AE1..0x0AE4 are distinctive, so a bare push is a
medium-confidence fallback. The common literals 0x01, 0x100, 0x0AE5 count only
when resolved as an actual CALL target.
## What actually ran (real results, this session)
`node selftest.js` (no network) ALL PASS:
- keccak256("") == c5d2460186f7233c... (zero-dep keccak is correct).
- predictPqcAccountAddress re-derived the LIVE sample AerePQCAccount address
0xa42a5e7F72E46BadC11367650Ec34D676194326f from its real 897-byte Falcon key
(CREATE2 math byte-correct vs a real deployment).
- Scanner on 4 REAL bytecode fixtures: AerePQCAttestation GREEN, AerePQCTxAccount
GREEN, AereHybridAuth RED (Solidity-verifier scope limit), AereFalcon512Verifier
RED (0x0100 are literals). Synthetic hybrid YELLOW; empty code RED (EOA).
Live against https://rpc.aere.network (the --rpc path is NOT [MEASURE]; it ran):
- AerePQCAttestation 0x465d... => GREEN, chainId 2800, 4402 bytes (matches fixture).
- Foundation EOA 0x0243... => RED, 0 bytes of code.
- createAccount(bytes,uint256) selector 0x0fd8377b computed by the zero-dep
keccak is present in the live factory's deployed bytecode.
## Scope boundary (restated in tool output)
Migrating account authentication to a PQC scheme does NOT make Aere consensus
post-quantum. Mainnet 2800 still seals blocks with classical secp256k1 QBFT, and
the ZK verifiers (BN254 Groth16) are classical.
## [VERIFY] / [MEASURE] flags carried
- [VERIFY] AereHybridAuth is repo source only; no mainnet address in the address
book as of 2026-07-19, so the hybrid path's deploy step is founder/deployer
gated (flagged in plan output).
- [VERIFY] Scanner cannot see Falcon verification done via the SOLIDITY
AereFalcon512Verifier (a contract call, not a precompile), so such contracts
read RED; confirm auth paths against source.
- [VERIFY] ecrecover (0x01) is resolved by stack sim, but the literal 1 is
common; a contract with no signature precompile call correctly reads RED.
- [VERIFY] Testnet-only precompiles 0x0AE6..0x0AE8 are not on mainnet 2800.
- [MEASURE] Only the live scan needs network; offline scans and the whole
self-test run with no RPC.
## Founder-gated caveat
The AerePQCAccountFactory (0xd5315Ea7...CE58) is live and createAccount is
permissionless, so deploying a PQC account is possible today. Deploying NEW
contracts or a corrected V2 redeploy of an account factory is signed by the
Foundation/deployer key, not by this toolkit.
## Files
scan.js, plan.js, simulate.js, selftest.js, README.md, package.json,
lib/{keccak,bytecode,scanner,migrate,simulate,precompiles,rpc}.js,
fixtures/{AerePQCAttestation,AerePQCTxAccount,AereHybridAuth,AereFalcon512Verifier}.hex
+ fixtures/index.json (real deployed bytecode extracted from repo artifacts).

View File

@ -0,0 +1,521 @@
# Aere Network: new-contracts deployment-readiness runbook
Scope: the ~17 new Solidity contracts added under `aerenew/contracts/contracts/` during this build loop.
Source review: `aerenew/docs/AERE-SECURITY-REVIEW-NEW-CONTRACTS.md` (no CRITICAL, no HIGH).
Applied fixes: `aerenew/docs/AERE-SECURITY-FIXES.md` (M1 fixed, L1 x3 fixed, L2 fixed, L3 documented).
Solc 0.8.23, OpenZeppelin v4.9.x.
STATUS: NOTHING HERE IS DEPLOYED. Every contract below is founder-gated. This document is the exact,
ordered procedure to deploy and verify each one WHEN the founder greenlights it. It does not deploy
anything. Confirmed on 2026-07-19 that none of these 17 contracts has a `deployments/*.json` entry and
none appears in `aerenew/sdk-js/src/addresses.ts`. Activation is the founder's decision, and where noted
it also requires an external audit and, for three of them, a zk circuit that does not yet exist.
---
## 0. Safety rules (read first, applies to every step)
1. DEPLOY FROM THE DESIGNATED DEPLOYER ONLY. The frontier / inert deployer key is
`0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465`. Ownable contracts start owned by this key and are then
transferred to the Foundation `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` (or the Timelock) per the
per-contract founder actions below. Do not deploy from any other key.
2. SECRET-SCAN THE REPO BEFORE ANY PUBLISH. Run the clean-repo secret scan before pushing source,
verification metadata, or any artifact to a public location. No keys, no infra hostnames.
3. NEVER TOUCH THE LIVE VALIDATORS OR THE LIVE INFRA BOX. Do not run heavy compute, provers, or deploy
jobs on the infra box that serves the live RPC / explorer / relayer / oracle. Deploy transactions are
submitted to `https://rpc.aere.network`; proving and vkey derivation run on a dedicated non-infra box
that is deleted after use. Confirm RPC stays sub-second after any activity.
4. NO NEW TOKEN. None of these contracts introduces an ERC-20 or a wrapped/governance token, and none may
be modified to do so. Rewards and payments settle in native AERE or an existing allowlisted ERC-20.
5. NEVER MOVE RESERVE OR FOUNDATION FUNDS. These deploys spend only gas from the deployer key. No transfer
of Mining Reserve, Ecosystem Reserve, Team Reserve, Airdrop Reserve, or Foundation holdings is part of
any step here.
6. CODE-READY IS NOT ACTIVATION. Passing tests and a clean self-review make a contract code-ready. Going
live on mainnet 2800 is a separate founder decision, and for the fund-flow contracts and the
circuit-gated verifiers it is also gated on an external audit and, respectively, on a real SP1 circuit.
7. NONE OF THESE CHANGES CONSENSUS. Every contract is application / account / settlement layer. Mainnet
2800 still seals blocks with classical secp256k1 QBFT. The Falcon / ML-DSA precompiles and the SP1
gateway are application-layer primitives. Never describe any of this as making consensus post-quantum.
---
## 1. Live addresses these contracts wire to
All values below are LIVE on mainnet 2800 and were read from `aerenew/sdk-js/src/addresses.ts` and
`aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md`. Pass these EXACT values as constructor arguments where a
contract needs them.
| Ref | Live address | Notes |
|-----|--------------|-------|
| Falcon-512 precompile | `0x0AE1` | Live, activated block 9,189,161. Used by name in-contract (constant), not a constructor arg. |
| ML-DSA-44 precompile | `0x0AE3` | Live, activated block 9,189,161. Used by name in-contract (constant). |
| SP1VerifierGateway | `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628` | Stable Succinct gateway; `verifyProof` reverts on an invalid proof. |
| AerePQCKeyRegistry | `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691` | PQC public-key registry; `verifyWithKey` / `statusOf` / `schemeOf`. |
| AereAgentDID | `0xce641d7d7C10553D82b06B7C21d423550e7522C5` | Falcon-rooted agent identity; `agentExists` / `getAgent` / `keyRegistry`. |
| AereAIReputation | `0x781ef746c08760aa854cDa4621d54db6734bfeBF` | Composable reputation; attestor set is Foundation-only and immutable. |
| AerePQCAccountFactory | `0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58` | CREATE2 factory; `predictAddress(falconPubKey, salt)`. Runtime ref for the migrator, not a constructor arg. |
| AERE402FacilitatorV2 | `0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de` | Canonical agentic settlement rail; `settle(...)`. (V1 `0xbA6e…4E56` is deprecated, do not wire.) |
| AereAgentV2 | `0x3FAcb997eb4252341052e7c84a86fCa0513c4490` | Machine-account registry the facilitator debits. |
| Foundation | `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` | Recommended owner / arbiter / governance target. Single-key EOA today, not a multisig. |
| Deployer | `0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465` | The only key these are deployed from. |
| AereZkQbftLightClient | `0xCaDA54FAb6E7AE311d240Cf0C2Df45e974156488` | Origin-side finality client the destination settler's repayment path composes with (reference, not a constructor arg). |
| AereOutboundVerifierV2 | `0x08b68bd553116Dffb99E648cb764AA93930da96F` | Origin-side inclusion-proof pattern the settler documents (reference only). |
---
## 2. Topologically-ordered deploy list
Deploy in this order. A contract may be deployed only after every contract it takes as a constructor
argument already exists. Contracts inside the same wave have no dependency on each other.
WAVE A (no constructor dependency on any sibling; wire only to live addresses or nothing):
- A1. `pqc/AereAccountMigrator` (no constructor args)
- A2. `oracle/AereRandomnessBeaconV2` (no constructor args; the BLS library is internal / inlined, no linking)
- A3. `AereRecoveryRegistry` (no constructor args)
- A4. `intents/AereDestinationSettler` (no constructor args)
- A5. `pqfinality/AerePQAttestationKeyRegistry` (no constructor args; Ownable, owner = deployer)
- A6. `compliance/AereBitstringStatusList(nonRevocationVerifier)` (pass `address(0)`, see B/notes)
- A7. `erc8004/AereIdentityRegistry8004(did)` (wires to live AereAgentDID)
- A8. `erc8004/AereValidationRegistry8004(keyRegistry, did)` (wires to live registry + DID)
- A9. `erc8004/AereAP2MandateVerifier(keyRegistry, settlementExecutor)` (wires to live registry)
- A10. `compliance/AereTrustRegistry(keyRegistry)` (wires to live registry; Ownable)
- A11. `agentic/AereVectorStore(facilitator)` (wires to live AERE402FacilitatorV2)
- A12. `depin/AereComputeMarketV3(zkVerifier, arbiter, governance)` (wires to live SP1 gateway + Foundation)
WAVE B (depends on a Wave A output):
- B1. `erc8004/AereReputationRegistry8004(reputation, identity)` (identity = A7)
- B2. `compliance/AereVerifiableCredential(trustRegistry, statusList)` (trustRegistry = A10, statusList = A6)
WAVE C (CIRCUIT-GATED, do NOT deploy until the SP1 circuit and its real vkey exist):
- C1. `mpc/AerePQAggregateVerifier(gateway, aggregateProgramVKey)` (gateway = live SP1; vkey is [VERIFY])
- C2. `pqfinality/AereFinalityCertificateVerifier(gateway, programVKey, registry)` (registry = A5; vkey is [VERIFY])
WAVE D (depends on a Wave C output, so also circuit-gated):
- D1. `modular/AerePQAggregateModule(verifier)` (verifier = C1)
Note: A6 through A12 have no dependency on A1 through A5; they are grouped in Wave A only because their
sole dependencies are LIVE addresses. B1/B2 must wait for A7 / A10 / A6. C1/C2/D1 must wait for their real
vkeys and, for C2 and D1, for A5 / C1.
---
## 3. Gate classification
| Contract | Gate | Rationale |
|----------|------|-----------|
| AereAccountMigrator | DEPLOY-WHEN-READY (external audit recommended: moves user funds) | Pure conduit, no custody, no admin. Review verdict CLEAN. |
| AereRandomnessBeaconV2 | DEPLOY-WHEN-READY | Real EIP-2537 BLS verify, fail-closed, no admin. CLEAN. |
| AereRecoveryRegistry | DEPLOY-WHEN-READY | Append-only, no admin, Falcon fail-closed. CLEAN (I4 cosmetic). |
| AereDestinationSettler | DEPLOY-WHEN-READY; repayment loop is [MEASURE] | Contract deploys with zero args; end-to-end solver repayment needs an off-chain SP1 prover + an origin-chain verifier that do not exist yet. External audit recommended (fund-flow). |
| AereIdentityRegistry8004 | DEPLOY-WHEN-READY | Controller-gated metadata over the live DID. CLEAN. |
| AereValidationRegistry8004 | DEPLOY-WHEN-READY | PQC-verified validation, no admin. CLEAN (I5 spam note). |
| AereAP2MandateVerifier | FOUNDER-CONFIG at deploy: `settlementExecutor` MUST be non-zero | L3: the open path (executor == 0) is replayable and is TEST-ONLY. Production must pin the AERE402 executor. |
| AereTrustRegistry | FOUNDER-ACTION: transfer owner to Foundation, then accredit issuers | Ownable trusted-list operator by design. CLEAN. |
| AereVerifiableCredential | DEPLOY-WHEN-READY (after A10 + A6) | Binds accreditation + PQC + validity + revocation, fail-closed. CLEAN. |
| AereBitstringStatusList | DEPLOY-WHEN-READY; zk non-revocation path is CIRCUIT-GATED | Deploy with verifier `address(0)`; the SP1 non-revocation circuit is [MEASURE], so `nonRevocationVKey` stays 0 (path fail-closed OFF) until it exists. CLEAN (I2 by-design). |
| AereComputeMarketV3 | EXTERNAL-AUDIT-GATED (highest-value fund flow) + FOUNDER-CONFIG (arbiter, governance) | Escrow + disputes + PQC settlement. Review CLEAN, L1 fixed, but this is the one contract that custodies escrowed rewards and bonds. |
| AereVectorStore | DEPLOY-WHEN-READY (external audit recommended: routes AERE402 funds) | L2 fixed (receipt bound to provider). Settles on the live rail, custodies no balance across calls. |
| AerePQAttestationKeyRegistry | FOUNDER-ACTION: enroll validators, each validator registers own PQ key; owner to Timelock later | Append-only, no key-forgery path. CLEAN. Only meaningful in concert with C2. |
| AereReputationRegistry8004 | FOUNDER-ACTION for WRITE-THROUGH: register adapter as attestor on a reputation instance; owner to Foundation, add feedback authors | M1 fixed (owner-curated authors). READ path works against the live instance today; write-through needs a Foundation governance step. |
| AerePQAggregateVerifier | DEPENDENCY-GATED: do NOT deploy until the SP1 aggregation circuit exists and its real vkey is known | `aggregateProgramVKey` is IMMUTABLE. The ML-DSA-65 aggregation circuit is [MEASURE], not built. L1 fixed (code-less gateway guard). |
| AereFinalityCertificateVerifier | DEPENDENCY-GATED: do NOT deploy until the PQ-finality aggregation circuit exists and its real vkey is known | `programVKey` is IMMUTABLE. The circuit is [MEASURE], not built. Already carries the `VerifierHasNoCode` guard. |
| AerePQAggregateModule | DEPENDENCY-GATED (through C1) | Cannot be meaningfully deployed before C1, which is circuit-gated. CLEAN. |
Summary:
- DEPLOY-WHEN-READY (low risk, code-ready today): AereAccountMigrator, AereRandomnessBeaconV2,
AereRecoveryRegistry, AereDestinationSettler, AereIdentityRegistry8004, AereValidationRegistry8004,
AereVerifiableCredential, AereBitstringStatusList, AereVectorStore.
- FOUNDER-CONFIG / FOUNDER-ACTION before they are useful: AereAP2MandateVerifier (executor),
AereTrustRegistry (owner + accredit), AereReputationRegistry8004 (attestor grant),
AerePQAttestationKeyRegistry (enroll + keys), AereComputeMarketV3 (arbiter + governance).
- EXTERNAL-AUDIT-GATED before mainnet (fund flow): AereComputeMarketV3 (strongest), and recommended for
AereVectorStore, AereDestinationSettler, AereAccountMigrator.
- MUST NOT DEPLOY until a circuit exists: AerePQAggregateVerifier, AereFinalityCertificateVerifier,
AerePQAggregateModule, and the zk non-revocation feature of AereBitstringStatusList.
---
## 4. Per-contract runbook
Each entry: constructor arguments (with the real value to pass), deploy order / dependencies, post-deploy
verification, and the gate. Post-deploy `eth_getCode` should be non-empty for every contract; that check
is implied and not repeated each time.
### A1. pqc/AereAccountMigrator
- Constructor arguments: NONE. `contract AereAccountMigrator is ReentrancyGuard` has no constructor.
- Dependencies / order: none. Wave A.
- Runtime references (not constructor args): `migrateToPqcAccount` and `predictPqcAccount` take a `factory`
argument at call time. Pass the live AerePQCAccountFactory `0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58`.
- Post-deploy verification:
- Confirm NO admin: the ABI has no `owner()`, no `withdraw` / `rescue` / `sweep`. A call to `owner()`
must revert (function does not exist).
- `predictPqcAccount(0xd531…CE58, <897-byte Falcon pubKey>, <salt>)` returns the same address as the live
factory's own `predictAddress(pubKey, salt)` (CREATE2 parity).
- A `migrate` with `moveNative=false` and non-zero `msg.value` reverts `StrayNative`; with an empty token
array and `moveNative=false` reverts `NothingToMigrate`.
- Run `aerenew/contracts/test/account-migrator.test.js` against a fork / live-like config.
- Gate: DEPLOY-WHEN-READY. External audit recommended because it moves user balances (no custody, but it
is the tx that sweeps an EOA). Verdict CLEAN.
### A2. oracle/AereRandomnessBeaconV2
- Constructor arguments: NONE. Constants only (`DRAND_CHAIN_HASH`, `DRAND_PERIOD`, `DRAND_GENESIS`). The
`AereDrandQuicknetBLS` library is an internal library (all functions `internal` / `private`), so it is
inlined at compile time and needs no separate deployment or `--libraries` linking.
- Dependencies / order: none. Wave A. Relies on live EIP-2537 precompiles `0x05`, `0x0b`, `0x0f`, `0x10`.
- Post-deploy verification:
- `DRAND_CHAIN_HASH()` == `0x52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971`,
`DRAND_PERIOD()` == 3, `DRAND_GENESIS()` == 1692803367.
- `verifyRound(round, sig)` with a REAL drand quicknet round returns true; with a tampered signature
returns false (fail-closed). Confirm NO admin (no verification-off switch, no `owner()`).
- Run `aerenew/contracts/test/AereRandomnessBeaconV2.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN. Honest note: drand BLS is classical, verifying it does not make the
randomness post-quantum.
### A3. AereRecoveryRegistry
- Constructor arguments: NONE. Falcon-512 precompile `0x0AE1` is a hard-coded constant
(`PRECOMPILE_FALCON512`).
- Dependencies / order: none. Wave A. Requires the live Falcon-512 precompile (block 9,189,161+).
- Post-deploy verification:
- `PRECOMPILE_FALCON512()` == `0x0AE1`; `RECORD_DOMAIN()` == `keccak256("AereRecoveryRegistry.v1.record")`.
- Register a watchtower Falcon key via `registerOperator(pubKey)`, read back `getOperator` / `nonceOf`.
- `submitRecovery` with a valid Falcon signature over `recordChallenge(...)` appends a record and bumps
the operator nonce; a tampered signature reverts `PQCVerificationFailed` and appends nothing.
- Confirm NO owner / admin (append-only, no `owner()`).
- Run `aerenew/contracts/test/AereRecoveryRegistry.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN (I4: `getRecord` on a bad id reverts `UnknownOperator`, cosmetic).
### A4. intents/AereDestinationSettler
- Constructor arguments: NONE. `AERE_CHAIN_ID` (2800) and `FILLED_EVENT_SIG` are constants.
- Dependencies / order: none for the deploy. Wave A.
- Post-deploy verification:
- `AERE_CHAIN_ID()` == 2800; `FILLED_EVENT_SIG()` ==
`keccak256("Filled(bytes32,address,address,uint256,address,bytes32,uint64,uint64)")`.
- A `fill` where the delivered token / recipient do not match the declared output reverts `OutputMismatch`;
a second `fill` of the same order reverts `AlreadyFilled`; a good fill emits `Filled`, records the
`FillRecord`, and `fillCommitment(orderId)` returns a non-zero binder.
- Confirm NO custody / admin (solver-to-recipient direct transfer, no `owner()`).
- Run `aerenew/contracts/test/destination-settler.test.js`.
- Gate: DEPLOY-WHEN-READY for the on-chain filling side. The origin-chain repayment loop (SP1 prover over
the `qbft-lightclient-guest` ELF plus an AereOutboundVerifierV2-style verifier deployed on the origin
chain) is [MEASURE] and NOT part of this deploy; the settler is complete without it, but a solver is not
actually repaid until that off-chain / second-chain machinery exists. External audit recommended
(fund-flow, even though this contract takes no custody). CLEAN (I1: fee-on-transfer output token note).
### A5. pqfinality/AerePQAttestationKeyRegistry
- Constructor arguments: NONE. `is Ownable`; OpenZeppelin `Ownable` sets `owner = msg.sender` (the deployer).
- Dependencies / order: none for the deploy. Wave A. It is a constructor dependency of C2
(AereFinalityCertificateVerifier), so it must exist before C2.
- Post-deploy verification:
- `owner()` == deployer; `validatorCount()` == 0; `validatorSetRoot()` is a deterministic value over an
empty set.
- After the founder action below: `validators()` returns the enrolled set in enrollment order,
`isSetComplete()` becomes true once every enrolled validator has a key, and `validatorSetRoot()` changes
on each enroll / key rotation.
- Confirm a non-validator cannot register a key for another address (`registerAttestationKey` uses
`msg.sender`), and a non-owner `enrollValidator` reverts `Ownable: caller is not the owner`.
- Run `aerenew/contracts/test/AerePQFinalityCertificate.test.js` (covers registry + verifier).
- Gate: FOUNDER-ACTION. The owner (Foundation, later the Timelock, founder-signed) enrolls the validator
addresses; then EACH validator registers its OWN hash-based attestation key. Transfer ownership to the
Foundation / Timelock after deploy. CLEAN. Note: this registry is only useful once C2 is live, which is
circuit-gated.
### A6. compliance/AereBitstringStatusList
- Constructor arguments:
1. `nonRevocationVerifier_` (address): PASS `address(0)`. This keeps the zk non-revocation path
fail-closed OFF (`nonRevocationConfigured()` stays false) until the SP1 non-revocation circuit exists.
The constructor guards a NON-ZERO but code-less address with `VerifierHasNoCode` (L1 fix); the zero
address is explicitly allowed. Only pass the live SP1 gateway `0x9ca4…0628` here if you intend to wire
the zk path, and even then the vkey stays 0 until the circuit is built.
- `ADMIN` is set to `msg.sender` (the deployer) and is IMMUTABLE; it cannot be transferred. It can only
publish the non-revocation vkey, nothing else.
- Dependencies / order: none for the deploy. Wave A. It is a constructor dependency of B2
(AereVerifiableCredential), so it must exist before B2.
- Post-deploy verification:
- `ADMIN()` == deployer; `NON_REVOCATION_VERIFIER()` == `address(0)` (if you passed zero);
`nonRevocationConfigured()` == false; `nonRevocationVKey()` == `bytes32(0)`.
- `createList(purpose, capacity)` returns a listId; `setStatus` / `revoke` are controller-only;
revocation bits are terminal (clearing reverts `RevocationIsTerminal`), suspension bits are reversible.
- `verifyNonRevocation(...)` reverts `NonRevocationNotConfigured` while the circuit is unset (so callers
can never mistake "unconfigured" for "not revoked").
- Run `aerenew/contracts/test/aere-pq-screen.test.js` (covers trust registry + VC + status list).
- Gate: DEPLOY-WHEN-READY for the bitstring mechanism. The zk non-revocation feature is CIRCUIT-GATED:
`ADMIN` calls `setNonRevocationVKey(<real vkey>)` only once the SP1 non-revocation circuit is built.
CLEAN (I2: status roots are controller-attested by design; `getWord` allows independent recomputation).
### A7. erc8004/AereIdentityRegistry8004
- Constructor arguments:
1. `did` (address): the live AereAgentDID `0xce641d7d7C10553D82b06B7C21d423550e7522C5`. The constructor
reads `keyRegistry()` from the DID and stores it as `KEY_REGISTRY`, so you do NOT pass the key registry
separately. It reverts `ZeroAddress` on a zero DID or a DID that returns a zero key registry.
- Dependencies / order: needs the live AereAgentDID. Wave A. It is a constructor dependency of B1
(AereReputationRegistry8004), so it must exist before B1.
- Post-deploy verification:
- `DID()` == `0xce64…22C5`; `KEY_REGISTRY()` == `0x1eCa…3691` (confirms the DID rooted into the expected
live key registry).
- `register(agentId, domain, cardURI)` succeeds only for `msg.sender == KEY_REGISTRY.ownerOf(agentId)`
(the current Falcon-root controller) and reverts `NotController` otherwise; `resolveByDomain` /
`getAgent` return the bound metadata; `didString(agentId)` == `did:aere:2800:<agentId>`.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/erc8004-adapters.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN. [VERIFY] against the finalized ERC-8004 identity interface: Aere anchors
the id to the Falcon-rooted DID id rather than minting a sequential ERC-721 id (documented divergence).
### A8. erc8004/AereValidationRegistry8004
- Constructor arguments:
1. `keyRegistry_` (address): live AerePQCKeyRegistry `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`.
2. `did_` (address): live AereAgentDID `0xce641d7d7C10553D82b06B7C21d423550e7522C5`.
Reverts `ZeroAddress` on either being zero.
- Dependencies / order: needs live registry + DID. Wave A.
- Post-deploy verification:
- `KEY_REGISTRY()` == `0x1eCa…3691`; `DID()` == `0xce64…22C5`.
- `precompileFor(1)` == `0x0AE1` (Falcon-512), `precompileFor(3)` == `0x0AE3` (ML-DSA-44).
- `requestValidation(validatorKeyId, serverAgentId, dataHash)` requires an ACTIVE Falcon/ML-DSA validator
key and an existing server agent; `respondValidation` accepts only a valid PQC signature over
`responseChallenge(...)` and reverts `PQCValidationFailed` on a tampered proof; a request completes
exactly once.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/erc8004-adapters.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN (I5: permissionless request creation is storage-spam only, harmless).
### A9. erc8004/AereAP2MandateVerifier
- Constructor arguments:
1. `keyRegistry_` (address): live AerePQCKeyRegistry `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`.
2. `settlementExecutor_` (address): [VERIFY: supply the AERE402 settlement-executor that will call
`authorizeSpend` atomically with settlement. MUST be non-zero in production.] Passing `address(0)`
enables the OPEN accounting path, which the security review (L3) and the fixes doc flag as TEST-ONLY:
a public standing Falcon signature is replayable by anyone to exhaust a mandate's cap. There is no live
facilitator today that calls `authorizeSpend`, so this address is a founder integration decision (the
candidate is AERE402FacilitatorV2 `0xFC2f…91de` or a purpose-built executor once wired). `SETTLEMENT_EXECUTOR`
is IMMUTABLE, so this is a deploy-time choice, not a later setter.
- Dependencies / order: needs live registry. Wave A. The executor address should exist first if a real
contract is used.
- Post-deploy verification:
- `KEY_REGISTRY()` == `0x1eCa…3691`; `SETTLEMENT_EXECUTOR()` == the non-zero executor you passed.
- `verifyMandate(m, sig)` (view) returns true for a valid Falcon mandate signature, false otherwise
(never reverts).
- `authorizeSpend(m, amount, sig)` from an address other than `SETTLEMENT_EXECUTOR` reverts
`NotSettlementExecutor`; from the executor with a valid signature within the window it records the spend
and enforces the cap; a cap overflow reverts `MandateCapExceeded`.
- Confirm NO owner / admin and NO fund custody (cap is accounting only).
- Run `aerenew/contracts/test/erc8004-adapters.test.js` (includes the settlement-executor-gate test).
- Gate: FOUNDER-CONFIG at deploy. Production MUST pass a non-zero `settlementExecutor_`. CLEAN except the
documented L3 open-path risk, which the non-zero executor closes.
### A10. compliance/AereTrustRegistry
- Constructor arguments:
1. `keyRegistry_` (address): live AerePQCKeyRegistry `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`.
`is Ownable`; owner = deployer at construction. The constructor also reserves issuer index 0 as an inert
placeholder.
- Dependencies / order: needs live registry. Wave A. It is a constructor dependency of B2
(AereVerifiableCredential), so it must exist before B2.
- Post-deploy verification:
- `KEY_REGISTRY()` == `0x1eCa…3691`; `owner()` == deployer; `issuerCount()` == 0; `NO_KEY()` ==
`type(uint256).max`.
- After ownership transfer + accreditation: `accreditIssuer` is owner-only; `isAccredited(id, type)` is
true only for an ACTIVE issuer with the type in scope; `revokeIssuer` is terminal (a revoked issuer can
never be reinstated); `verifyIssuerSignature` is fail-closed.
- A non-owner `accreditIssuer` / `suspendIssuer` / `revokeIssuer` reverts `Ownable: caller is not the owner`.
- Run `aerenew/contracts/test/aere-pq-screen.test.js`.
- Gate: FOUNDER-ACTION. After deploy, transfer ownership to the Foundation (the trusted-list operator),
then accredit real issuers. CLEAN. Deploying does not assert any real institution is accredited.
### A11. agentic/AereVectorStore
- Constructor arguments:
1. `facilitator` (address): the live AERE402 settlement rail. PASS AERE402FacilitatorV2
`0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de` (canonical; V1 `0xbA6e…4E56` is deprecated). [VERIFY:
confirm the deployed facilitator's `settle(uint256,address,address,uint256,bytes32,uint256,uint256,bytes)`
ABI matches the `IAERE402Facilitator` interface before relying on paid queries. The store only calls
`settle`; the `consumed` view in the interface is not called.] Reverts `ZeroAddress` on zero.
- Dependencies / order: needs the live facilitator. Wave A.
- Post-deploy verification:
- `FACILITATOR()` == `0xFC2f…91de`; `COMMIT_DOMAIN()` and `QUERY_DOMAIN()` match the constants.
- `createStore(...)` fixes owner + pricing at creation (no setters); a post-quantum store's `commit`
requires a valid Falcon signature over `commitChallenge(...)` (fail-closed), a classical store's requires
`msg.sender == owner`.
- L2 fix check: `paidQuery(..., provider, ...)` records the receipt bound to `provider`; `attestRetrieval`
by any address other than that provider reverts `NotReceiptProvider` and leaves the receipt unconsumed;
the bound provider consumes it successfully.
- Confirm NO owner / admin and no balance held across calls (delta-measured forward to recipient).
- Run `aerenew/contracts/test/aere-vector-store.test.js`.
- Gate: DEPLOY-WHEN-READY. External audit recommended (routes AERE402 payments). CLEAN with L2 fixed.
### A12. depin/AereComputeMarketV3
- Constructor arguments:
1. `zkVerifier` (address): live SP1VerifierGateway `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`. The
constructor reverts `VerifierHasNoCode` if this address has no code (L1 fix), so it MUST be the live
gateway.
2. `arbiter` (address): [FOUNDER DECISION. Recommended: Foundation `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`.]
Can only route a DISPUTED job's escrow; cannot drain. IMMUTABLE.
3. `governance` (address): [FOUNDER DECISION. Recommended: Foundation `0x0243…f3C3`.] Can only flip the
ERC-20 reward-token allowlist; cannot move funds. IMMUTABLE.
Reverts `ZeroAddress` on any of the three being zero.
- Dependencies / order: needs the live SP1 gateway (code check) and the two chosen roles. Wave A.
- Post-deploy verification:
- `ZK_VERIFIER()` == `0x9ca4…0628`; `ARBITER()` == chosen arbiter; `GOVERNANCE()` == chosen governance;
`FALCON512_PRECOMPILE()` == `0x0AE1`.
- `setTokenAllowed` reverts `NotGovernance` for a non-governance caller; `resolveDispute` reverts
`NotArbiter` for a non-arbiter caller.
- `isSolvent(address(0))` and `isSolvent(<token>)` are true after any sequence of job lifecycle calls
(the per-asset solvency invariant: balance >= totalLiabilities). A ZK_VERIFIED job pays only inside
`submitResultZK` after `verifyProof`; a `pqcSettlement` job's payout requires a valid Falcon signature
over `settlementDigest(...)` or reverts `NotPQCAuthorized`.
- Confirm NO owner and no sweep (only arbiter routing + governance allowlist).
- Run `aerenew/contracts/test/AereComputeMarketV3.test.js` (includes the L1 construction-guard test).
- Gate: EXTERNAL-AUDIT-GATED before mainnet (it is the highest-value fund-flow contract: escrowed rewards +
provider / challenger bonds). FOUNDER-CONFIG for arbiter and governance. CLEAN, L1 fixed.
### B1. erc8004/AereReputationRegistry8004
- Constructor arguments:
1. `reputation` (address): an AereAIReputation instance. For the READ path, pass the LIVE
`0x781ef746c08760aa854cDa4621d54db6734bfeBF`. For WRITE-THROUGH, pass the reputation instance that
LISTS THIS ADAPTER as an attestor. [VERIFY: the live instance's attestor set is Foundation-only and
immutable, so write-through requires a NEW AereAIReputation instance that includes this adapter; supply
that instance's address if write-through is intended.]
2. `identity` (address): the AereIdentityRegistry8004 deployed in A7.
`is Ownable`; owner = deployer. Reverts `ZeroAddress` on either being zero.
- Dependencies / order: needs A7 (identity adapter) and a reputation instance. Wave B.
- Post-deploy verification:
- `REPUTATION()` == the instance passed; `IDENTITY()` == the A7 address; `owner()` == deployer.
- `getScore(agentId)` / `getStats(agentId)` read straight from the live reputation (READ works today).
- M1 fix check: `giveFeedback` from an address NOT in `isAuthorizedFeedbackAuthor` reverts
`NotAuthorizedFeedbackAuthor` and writes nothing; after `addFeedbackAuthor(x)` (owner-only), `x` can
submit feedback in `{-1,0,+1}`; `removeFeedbackAuthor` re-blocks; only the owner manages the allowlist.
- `canWriteThrough()` is true only when the reputation instance lists this adapter as an attestor
(`AdapterNotAttestor` otherwise).
- Run `aerenew/contracts/test/erc8004-adapters.test.js`.
- Gate: FOUNDER-ACTION for write-through. READ path is deploy-when-ready against the live instance. For
write-through the Foundation must deploy / point a reputation instance that lists this adapter, then the
owner authorizes feedback authors. Transfer ownership to the Foundation. M1 fixed.
### B2. compliance/AereVerifiableCredential
- Constructor arguments:
1. `trustRegistry_` (address): the AereTrustRegistry deployed in A10.
2. `statusList_` (address): the AereBitstringStatusList deployed in A6.
Reverts `ZeroAddress` on either being zero.
- Dependencies / order: needs A10 and A6. Wave B.
- Post-deploy verification:
- `TRUST_REGISTRY()` == A10 address; `STATUS_LIST()` == A6 address; `DID_METHOD()` == `"did:aere"`;
`VC_DOMAIN()` matches the constant.
- `verifyPresented(c, sig)` returns true only when accreditation + Falcon signature + validity +
not-revoked all hold (fail-closed, never reverts). `anchorCredential` reverts `NotAccredited` /
`NotYetValid` / `Expired` / `SignatureInvalid` on the respective failure and records nothing;
`anchorCredentialWithDid` enforces the `did:aere:` prefix and the `subjectDidHash` match.
- `isValid(id)` flips to false the instant the issuer is suspended / revoked in A10 or the credential's
status bit is set in A6.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/aere-pq-screen.test.js`.
- Gate: DEPLOY-WHEN-READY (after A10 + A6). CLEAN.
### C1. mpc/AerePQAggregateVerifier
- Constructor arguments:
1. `gateway` (address): live SP1VerifierGateway `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`. The
constructor reverts `VerifierHasNoCode` if code-less (L1 fix), so it MUST be the live gateway.
2. `aggregateProgramVKey` (bytes32): [VERIFY: supply the REAL SP1 verification key of the audited ML-DSA-65
aggregation circuit at deploy time.] This value is IMMUTABLE and pins the one circuit this verifier will
ever accept. The circuit is [MEASURE] in `docs/AERE-PQ-AGGREGATE.md` and is NOT implemented, so a real
vkey does not exist yet. Deploying now would permanently pin a placeholder. Reverts `ZeroVKey` on zero.
- Dependencies / order: Wave C. It is a constructor dependency of D1 (AerePQAggregateModule).
- Post-deploy verification (only meaningful once a real circuit + vkey exist):
- `SP1_GATEWAY()` == `0x9ca4…0628`; `AGGREGATE_PROGRAM_VKEY()` == the pinned real vkey.
- `registerCommittee(root, t, n, scheme)` enforces `1 <= t <= n` and a non-zero scheme; `verifyAggregate`
is fail-closed on every binding mismatch (root / threshold / size / scheme / digest / below-threshold)
and delegates the one cryptographic check to `verifyProof`.
- Confirm NO owner / admin and no fund custody.
- Run `aerenew/contracts/test/AerePQAggregate.test.js` (uses a MOCK gateway; the real circuit is not
exercised, so passing tests do NOT prove end-to-end aggregation).
- Gate: DEPENDENCY-GATED. DO NOT DEPLOY until the SP1 aggregation circuit exists and its real vkey is
derived on a dedicated non-infra box. L1 fixed.
### C2. pqfinality/AereFinalityCertificateVerifier
- Constructor arguments:
1. `gateway` (address): live SP1VerifierGateway `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`. Reverts
`VerifierHasNoCode` if code-less (guard already present).
2. `programVKey` (bytes32): [VERIFY: supply the REAL SP1 vkey of the audited PQ-finality aggregation
circuit at deploy time.] IMMUTABLE; pins the one circuit. The circuit is [MEASURE] in
`docs/AERE-PQ-FINALITY-CERTIFICATE.md` and is NOT implemented. Reverts `ZeroVKey` on zero.
3. `registry` (address): the AerePQAttestationKeyRegistry deployed in A5. Reverts `ZeroRegistry` on zero.
The constructor also derives `attestationDomain = keccak256(DOMAIN_PREFIX, chainid, registry)`.
- Dependencies / order: needs A5 and a real vkey. Wave C.
- Post-deploy verification (only meaningful once a real circuit + vkey exist):
- `SP1_GATEWAY()` == `0x9ca4…0628`; `PROGRAM_VKEY()` == the pinned real vkey; `REGISTRY()` == A5 address;
`attestationDomain()` == `keccak256(abi.encode(DOMAIN_PREFIX, 2800, <A5 address>))`.
- `currentQuorumThreshold()` == ceil(2N/3) for the live registry set (N=7 gives 5). `verifyCertificate`
is fail-closed on root / size / domain / quorum mismatch; a rotation in A5 invalidates old certificates.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/AerePQFinalityCertificate.test.js` (MOCK gateway; real circuit not exercised).
- Gate: DEPENDENCY-GATED. DO NOT DEPLOY until the PQ-finality aggregation circuit exists and its real vkey
is known, AND A5 is deployed with the validator set enrolled and keyed. Additive to consensus, never a
consensus flip. CLEAN, already carries the `VerifierHasNoCode` guard.
### D1. modular/AerePQAggregateModule
- Constructor arguments:
1. `verifier` (address): the AerePQAggregateVerifier deployed in C1. Reverts `BadVerifier` on zero.
- Dependencies / order: needs C1. Wave D. Transitively circuit-gated.
- Post-deploy verification:
- `VERIFIER()` == C1 address; `isModuleType(1)` == true; `name()` == `"aere.pq-aggregate-validator.1.0.0"`.
- `onInstall(abi.encode(committeeId))` reverts `UnknownCommittee` unless the committee is registered in C1;
`validateUserOp` returns 0 only when the signature carries a valid aggregate proof for the account's
committee over `userOpHash`, else 1 (never reverts on a malformed signature).
- Confirm NO owner / admin (singleton, per-account state keyed by `msg.sender`).
- Run `aerenew/contracts/test/AerePQAggregate.test.js`.
- Gate: DEPENDENCY-GATED (through C1). [VERIFY] the ERC-7579 signature-unwrapping against the specific
account stack (Safe7579 / Kernel / Nexus) you install it under. CLEAN.
---
## 5. Consolidated [VERIFY] list (must be supplied at deploy time)
Addresses:
- AereAP2MandateVerifier constructor arg 2, `settlementExecutor_`: the non-zero AERE402 settlement-executor
that calls `authorizeSpend`. MUST NOT be `address(0)` in production (L3). Founder integration decision.
- AereReputationRegistry8004 constructor arg 1, `reputation`: `0x781e…feBF` for READ; for WRITE-THROUGH, a
reputation instance that lists this adapter as an attestor (the live instance's attestor set is immutable
and Foundation-only, so a new instance is required).
- AereVectorStore constructor arg 1, `facilitator`: recommended AERE402FacilitatorV2 `0xFC2f…91de`; confirm
the deployed `settle` ABI matches the interface.
- AereComputeMarketV3 constructor args 2 and 3, `arbiter` and `governance`: founder-chosen (Foundation
`0x0243…f3C3` recommended).
vkeys (IMMUTABLE, pin a circuit that does NOT yet exist, so these contracts must NOT deploy until the
circuit is built and its real vkey is derived on a dedicated non-infra box):
- AerePQAggregateVerifier constructor arg 2, `aggregateProgramVKey`: real SP1 vkey of the ML-DSA-65
aggregation circuit ([MEASURE]).
- AereFinalityCertificateVerifier constructor arg 2, `programVKey`: real SP1 vkey of the PQ-finality
aggregation circuit ([MEASURE]).
- AereBitstringStatusList post-deploy `setNonRevocationVKey(vkey)`: real SP1 vkey of the zk non-revocation
circuit ([MEASURE]); set by `ADMIN` (the deployer) only once the circuit exists. The contract itself
deploys today with `nonRevocationVerifier_ = address(0)`.
Interface confirmations ([VERIFY] against a finalized external spec, not a deploy blocker):
- ERC-8004 identity / reputation / validation selector conformance (the adapters keep ERC-8004 semantics
but anchor ids to the Falcon-rooted DID id by design).
- x402 / AP2 mandate field set (AereAP2MandateVerifier struct).
- W3C VC 2.0 JSON member mapping (AereVerifiableCredential anchors a digest, not the JSON-LD document).
- ERC-7579 signature unwrapping for the target account stack (AerePQAggregateModule).
---
## 6. What is NOT in this runbook
- No consensus change. None of these deploys touches validator keys, QBFT config, or genesis.
- No fund movement beyond deployer gas. No Reserve / Foundation transfer is part of any step.
- No off-chain build. The SP1 aggregation circuits (finality, committee, non-revocation) and the
destination-settler origin-side repayment prover are [MEASURE] research / engineering items tracked
separately; this runbook covers only the on-chain contract deploys and their verification.

View File

@ -0,0 +1,90 @@
# Aere Network: NIST Validation Status of the Post-Quantum Cryptography
Status: living document. Last verified against the repository on 2026-07-19.
## 1. What this document claims, and what it does not
Aere Network ships native post-quantum cryptographic (PQC) precompiles inside its execution client. This document states, precisely and checkably, the relationship between those implementations and the relevant U.S. National Institute of Standards and Technology (NIST) standards.
What Aere Network claims:
- Aere validates its precompile implementations bit-for-bit against the official NIST test vectors. Where the underlying standard is final, this includes NIST ACVP style vectors (Automated Cryptographic Validation Protocol test data published by NIST). Where the standard is not final, this means the published Known Answer Test (KAT) reference vectors only.
- Each validation runs in two directions: the harness feeds an official valid vector and asserts the precompile ACCEPTS it, and it feeds an official invalid vector (or a crafted byte-flip of a valid one) and asserts the precompile REJECTS it. A one-directional "accepts a good signature" check is not sufficient and is not what we rely on.
- The underlying cryptographic implementation is Bouncy Castle (bcprov-jdk18on-1.83.jar). Aere does not roll its own lattice or hash-based signature math for these schemes.
What this document explicitly does NOT claim:
- It does NOT claim that NIST audits, reviews, endorses, or has any relationship with Aere Network. NIST publishes standards and test vectors. Aere consumes them. That is the whole of the relationship.
- It does NOT claim that Aere holds a NIST certification, a FIPS certificate, or a CAVP or ACVP validation listing. Self-validating an implementation against published vectors is a real and reproducible engineering result. It is NOT the same as a NIST-run validation program certificate, and Aere is not listed on the NIST CAVP or ACVP validation lists.
- It does NOT claim that any external security firm has audited this code. An independent security audit (for example by Trail of Bits or NCC Group) is a separate, funded step that has not yet happened. See Section 4.
In one sentence: this is honest, reproducible self-validation against NIST-published vectors, not a NIST certification and not an external audit.
## 2. Per-scheme validation status
Notes that apply to the whole table:
- "Standard status" reflects real-world NIST status as of this writing. FIPS 203, 204, and 205 were finalized in August 2024. FIPS 206 (FN-DSA, the standardized form of Falcon) is still in draft and is expected to finalize around 2027.
- Repository paths are relative to the repository root. All five mainnet precompiles activated on Aere Network mainnet (chain 2800) at block 9,189,161. ML-KEM-768 is testnet only and is NOT active on mainnet.
| Scheme | NIST standard | Standard status | Vectors we validate against | Where in repo | Honest caveat |
|---|---|---|---|---|---|
| ML-DSA-44 (Dilithium2), precompile `0x0AE3` | FIPS 204 | FINAL (Aug 2024). ACVP vectors exist. | Official NIST ACVP ML-DSA-sigVer-FIPS204 vectors, test group 8 (internal interface, externalMu=false). Accept the valid vector, reject the official crafted-invalid vectors (modified message, bad z, bad commitment, bad hint) plus explicit byte-flips and malformed lengths. | Fixture: `aerenew/contracts/test/fixtures/mldsa44-acvp-tg8.json`. Harness (subset): `aerenew/contracts/test/mldsa44Verifier.test.js`. Full 15/15: `aerenew/contracts/scripts/test-mldsa-local.js`. Native-fork KAT result: `aerenew/parallel/combined-fork-binary/logs/nist_kat_bc183.txt` (15 match, 0 mismatch). | These are genuine ACVP sigVer vectors, so "ACVP-style validation" is accurate here. It remains self-validation, not a CAVP/ACVP certificate. |
| SLH-DSA-SHA2-128s (SPHINCS+-SHA2-128s-simple), precompile `0x0AE4` | FIPS 205 | FINAL (Aug 2024). ACVP vectors exist. | Official NIST ACVP SLH-DSA-sigVer-FIPS205 vectors, test group 31 (internal interface, preHash none). All 14 vectors reproduce NIST's expected testPassed value (accept the valid vectors, reject the invalid ones), plus byte-flip rejects. | Fixture: `aerenew/contracts/test/fixtures/sphincs-sha2-128s-acvp-tg31.json`. Harness: `aerenew/contracts/test/AereSphincsVerifier.test.js`. Native-fork KAT result: `nist_kat_bc183.txt` (14 match, 0 mismatch). | Genuine ACVP sigVer vectors. The fixture records the upstream ACVP-Server file SHA-256 hashes for reproducibility. Still self-validation, not a certificate. |
| ML-KEM-768, precompile `0x0AE6` | FIPS 203 | FINAL (Aug 2024). ACVP vectors exist. | Official NIST ACVP ML-KEM-encapDecap-FIPS203 vectors (deterministic encapsulation: given ek and m, assert ciphertext c and shared key k match). README records 25/25. | Fetcher: `aerenew/pqc-fork/kat/fetch_acvp_mlkem.py`. Runner: `aerenew/pqc-fork/kat/MlKemAcvpKat.java`. Committed vectors: `aerenew/pqc-fork/vectors/mlkem768_acvp.txt`. Result: `aerenew/pqc-fork/results/kat-results-mlkem.json`. | TESTNET ONLY. This precompile is NOT active on Aere mainnet. It was built and KAT-verified on an isolated single-validator QBFT testnet. Mainnet activation is founder-gated and has not happened. |
| SHAKE256, precompile `0x0AE5` | FIPS 202 | FINAL (long-standing). KAT/ACVP available. | FIPS 202 known-answer digests, for example the empty-string SHAKE256(32) value `0x46b9dd2b...ed5762f` and a short-message ("abc") digest. Asserted equal to the FIPS 202 reference output. | Solidity-level assertion in `aerenew/contracts/test/AereFalcon512Verifier.test.js`. On-chain eth_call check: `aerenew/parallel/combined-fork-binary/scripts/check5_precompiles.py`. Native-fork KAT result: `nist_kat_bc183.txt` (4 match, 0 mismatch). | What is committed and asserted here is a small set of FIPS 202 KAT digests, not the full NIST ACVP SHA-3/SHAKE test-group suite. Described honestly as KAT. `[VERIFY: if a claim of full ACVP SHAKE coverage is ever wanted, commit and run the complete NIST ACVP SHA-3 vector set.]` |
| Falcon-512, precompile `0x0AE1`; Falcon-1024, precompile `0x0AE2` | FIPS 206 (FN-DSA) | DRAFT / NOT FINALIZED (expected around 2027). No official ACVP path yet. | Round-3 / reference Known Answer Test vectors only (the official Falcon reference `falconNNN-KAT.rsp` from the round-3 submission). Accept the official KAT signature and a second KAT vector, reject a tampered signature, a tampered message, a tampered signed-message blob, and a valid signature under the wrong public key. | Falcon-512 fixture: `aerenew/contracts/test/falcon512_kat0.json`; harness: `aerenew/contracts/test/AereFalcon512Verifier.test.js`. Falcon-1024 fixture: `aerenew/contracts/test/falcon1024_kat0.json`; harness: `aerenew/contracts/test/AereFalcon1024Verifier.test.js`. Native-fork KAT result: `nist_kat_bc183.txt` (valid + reject for both). | HONEST KEY POINT. There is no final FN-DSA standard and no official ACVP validation path for Falcon yet. Aere validates Falcon against round-3 / reference KAT vectors only and CANNOT claim FIPS or ACVP validation for Falcon until FIPS 206 finalizes. Do not describe Falcon as "ACVP-validated" or "FIPS-validated" anywhere in public copy. |
### 2.1 The Falcon caveat, stated plainly
Falcon is the largest and most easily misstated item here, so it gets its own paragraph. The scheme that NIST selected and is standardizing as FN-DSA in FIPS 206 is still a draft. Until that document is final, NIST does not publish an ACVP validation suite for it, and no CAVP or ACVP certificate for Falcon can exist for anyone, Aere included. What Aere can and does do is validate its Falcon-512 and Falcon-1024 precompiles bit-for-bit against the official Falcon reference KAT vectors from the round-3 submission, in both the accept and reject directions. That is a real correctness check. It is not, and must never be presented as, FIPS or ACVP validation. When FIPS 206 finalizes and NIST publishes FN-DSA ACVP vectors, this row can be upgraded, and not before.
## 3. Validation method
### 3.1 Two-directional Known Answer Testing
Every scheme is checked in both directions, because a verifier that returns "accept" for all inputs would pass a one-directional test while being catastrophically broken:
1. ACCEPT direction: feed an official valid vector (public key, message, signature, and where applicable nonce). Assert the precompile returns true (accept).
2. REJECT direction: feed an official invalid vector where NIST expects failure, or take a valid vector and flip a single byte inside the signature, the message, the challenge, or substitute the wrong public key. Assert the precompile returns false (reject).
For the finalized signature schemes (ML-DSA-44 and SLH-DSA-128s) the reject vectors are the actual NIST ACVP negative test cases (modified message, bad z, bad commitment, bad hint, and similar), so the harness reproduces NIST's own expected pass/fail flag for every case rather than only checking hand-made tamperings.
### 3.2 Where the harness runs
Validation is performed at more than one layer, and all layers must agree:
- Solidity / EVM layer. Hardhat test suites deploy the on-chain verifier contracts and drive them against the committed JSON fixtures. Examples: `aerenew/contracts/test/mldsa44Verifier.test.js`, `aerenew/contracts/test/AereSphincsVerifier.test.js`, `aerenew/contracts/test/AereFalcon512Verifier.test.js`, `aerenew/contracts/test/AereFalcon1024Verifier.test.js`.
- Native precompile layer. The precompile implementations themselves (Bouncy Castle backed) are exercised by a Java KAT harness on a separate build host, driven by `aerenew/parallel/combined-fork-binary/scripts/run_kat.sh`, which compiles and runs the probe against `bcprov-jdk18on-1.83.jar`. The committed result of that run is `aerenew/parallel/combined-fork-binary/logs/nist_kat_bc183.txt`: Falcon-512 and Falcon-1024 valid plus reject, ML-DSA-44 15/15, SLH-DSA-128s 14/14, SHAKE256 4/4, zero mismatches.
- Live on-chain layer. After the fork cross, all five mainnet precompiles were exercised by direct `eth_call` against a running node (`aerenew/parallel/combined-fork-binary/scripts/check5_precompiles.py`), and a verify-and-record probe wrote accept/reject results into contract state (`aerenew/dryrun-results/dryrun/phase_postfork_kat.sh`), confirming that an invalid vector reverts on-chain and a valid one records success.
### 3.3 Underlying implementation
The signature and KEM math is provided by Bouncy Castle, specifically bcprov-jdk18on-1.83.jar, as recorded in `run_kat.sh` and in the build record `aerenew/parallel/combined-fork-binary/BUILD_INFO.txt`. Aere's contribution is the EVM precompile plumbing, the calldata encoding, the gas accounting, and the validation harness, not a new implementation of ML-DSA, SLH-DSA, Falcon, ML-KEM, or SHAKE.
## 4. What this is NOT
This section exists to prevent overclaiming. Each line is a thing an auditor, a bank, or a developer might reasonably ask about, answered honestly.
- This is NOT a NIST certification. NIST has not tested, reviewed, or certified Aere's code. Aere consumes NIST's published standards and test vectors; there is no other relationship, and none is implied.
- This is NOT a CAVP or ACVP validation listing. Aere is not on the NIST Cryptographic Algorithm Validation Program or Automated Cryptographic Validation Protocol validated lists. Passing published ACVP-style vectors in-house is reproducible evidence of correctness; it is not the government-run validation program, and it does not produce a certificate number.
- This is NOT an external code audit. No independent security firm has reviewed this code. The self-validation described here is engineering done by the Aere builders on their own work.
- This is NOT FIPS or ACVP validation for Falcon. FIPS 206 (FN-DSA) is a draft. Falcon is validated against round-3 / reference KAT vectors only, and cannot carry a FIPS or ACVP claim until the standard finalizes (expected around 2027).
- ML-KEM-768 is NOT live on mainnet. It is a testnet-only precompile. Any statement about Aere mainnet PQC must exclude ML-KEM confidentiality.
### 4.1 The path to an external audit
An independent third-party security audit is a distinct and necessary step that has not yet occurred. It is gated on funding, not on technical readiness. The intended path is a review by an established firm such as Trail of Bits or NCC Group, covering the precompile implementations, the calldata and gas handling, and the surrounding consensus and contract code. When such an audit is commissioned and completed, its scope, date, firm, and findings will be named here explicitly. Until then, this document should be read as self-validation evidence only, and any reader who requires third-party assurance should treat that assurance as pending.
## 5. Reproduce it yourself
The point of self-validation is that anyone can rerun it. The committed inputs (JSON fixtures and the ML-KEM vector file), the harnesses (Hardhat suites and the Java KAT scripts), and the result logs are all in the repository under `aerenew/contracts/test/`, `aerenew/contracts/scripts/`, `aerenew/pqc-fork/`, and `aerenew/parallel/combined-fork-binary/`. Reproduction instructions for the fork build and KAT run are in `aerenew/pqc-fork/README.md`.
---
### Appendix A: items flagged for human verification before publishing
- `[VERIFY]` The native Bouncy Castle KAT harness source (referred to as `Probe.java` in `run_kat.sh`) and the flat vector files it consumes (`falcon512.txt`, `falcon1024.txt`, `mldsa44.txt`, `slhdsa128s.txt`, `shake256.txt`, expected on a separate build host at `<work>/probe/` and `<fork>/probe/`) are NOT committed to this repository. What IS committed is the result log (`nist_kat_bc183.txt`), the JSON fixtures, the Solidity/JS harnesses, and the run scripts. Decide whether to commit `Probe.java` and the flat vector files so the native-layer run is fully reproducible from the repo alone.
- `[VERIFY]` SHAKE256 coverage is described as KAT (a small set of FIPS 202 known-answer digests), not full ACVP SHA-3/SHAKE suite coverage. If a stronger claim is ever wanted, commit and run the complete NIST ACVP SHA-3 vector set first.
- `[VERIFY]` The Falcon KAT provenance cited in the harness comment is `falcon512-KAT.rsp`, vector 0, SHA-256 `dd75c946...b5cd`, reproduced from the round-3 submission. Only the extracted vector-0 (and vector-1) JSON is committed, not the original `.rsp` file. Confirm the `.rsp` source hash independently if provenance is challenged.
- `[VERIFY]` Confirm the block number for mainnet precompile activation (9,189,161) and the precompile addresses `0x0AE1` through `0x0AE5` against the live chain before publishing, and confirm ML-KEM-768 (`0x0AE6`) and Falcon HashToPoint (`0x0AE7`) remain testnet only.

View File

@ -0,0 +1,217 @@
# Spec 29: Post-Quantum Governance
Aere Network, chain ID 2800. Design specification. Status: DESIGN plus a capability on already-deployed primitives. Not activated.
## Honest scope, stated first
This document specifies how Aere's governance layer becomes post-quantum-authenticated: votes signed with a post-quantum key, threshold approval by a post-quantum committee, and upgrades authorized by a post-quantum-signed governance action end to end.
Read the following before anything else in this document.
1. This is a DESIGN plus a capability built on primitives that are already deployed and live on mainnet. It is not, itself, a live governance system.
2. Aere's governance is INERT today. The Governor and Timelock own nothing and control nothing; live voting power is zero because no AERE is staked yet. This is true of the classical governance stack and it is equally true of the post-quantum design layered on top of it.
3. Activating any funds-bearing or control-bearing governance path is founder-gated and audit-gated, exactly like the rest of mainnet. Nothing in this document authorizes a transfer of ownership, a funding action, or a consensus change.
4. This is governance-layer authentication only. It does NOT make consensus post-quantum. Aere consensus remains classical secp256k1 ECDSA QBFT. See Section 5.
Where an address or claim is not independently confirmed on-chain, it is flagged `[VERIFY: ...]` inline.
## 1. The coherence argument
Aere leads on post-quantum cryptography at the application and account layer. Five signature and hash precompiles are live on mainnet (activated at block 9,189,161) in the `0x0AE1`..`0x0AE5` band: Falcon-512 (`0x0AE1`), Falcon-1024 (`0x0AE2`), ML-DSA-44 (`0x0AE3`), SLH-DSA-SHA2-128s (`0x0AE4`), and SHAKE256 (`0x0AE5`). On top of these sit a Falcon-owned smart account, a hybrid ECDSA-plus-Falcon authorizer, a post-quantum threshold account, and a post-quantum attestation registry, all verifying in full, on-chain, through those precompiles.
A chain that authenticates payments, identities, and custody with post-quantum signatures should not authenticate its own governance with classical-only signatures. If a future adversary with a cryptographically relevant quantum computer could forge the ECDSA signature that casts a decisive vote, or the ECDSA multisig that approves a queued upgrade, then governance is the weakest link on an otherwise post-quantum-forward chain. The control plane would be softer than the assets it controls.
This is the same class of problem as an earlier honesty fix on Aere, where a randomness surface that returned a fixed `true` was corrected so that what the system claimed and what the system did were the same thing. Here the incoherence is not a false return value but a cryptographic asymmetry: strong authentication everywhere except the layer that decides upgrades and spends. This document closes that gap by specifying three things:
- **Post-quantum-signed votes.** A voter authenticates a ballot with a Falcon-512 signature, verified by the live `0x0AE1` precompile, instead of, or in addition to, an ECDSA signature.
- **Post-quantum threshold approval.** The multisig control path over the Timelock (guardian veto, and optionally proposal and execution) is an `AereThresholdAccount`, a t-of-n Falcon committee, instead of a classical ECDSA multisig.
- **Post-quantum-authorized upgrades.** A parameter change or contract upgrade is authorized by a governance action that is post-quantum-signed from the ballot through to the Timelock-gated execution.
The result is a governance layer whose authentication strength matches the rest of the chain. What follows is deliberately explicit about which of these is a straightforward capability on primitives that already exist, and which is new contract work.
## 2. The existing primitives
Every building block this design needs is already deployed on chain 2800. This section is the inventory. Addresses are cited from the canonical registry (`aerenew/sdk-js/src/addresses.ts`) and the governance and threshold docs (`aerenew/docs/GOVERNANCE.md`, `aerenew/docs/THRESHOLD-PQC-2026-07-12.md`, `aerenew/docs/AUDIT-FINDING-THRESHOLD-DUPLICATE-KEYS-2026-07-15.md`).
### 2.1 The Governor and Timelock (INERT)
The canonical governance stack is a standard OpenZeppelin v4.9.6 Governor plus TimelockController, deployed and proven binding on a throwaway target, but staged: it owns nothing and must pass an external audit before the Foundation migrates any ownership to it.
| Contract | Address | Role | Owns anything real? |
| --- | --- | --- | --- |
| `AereGovernorV2` | `0x03251BD9A115385B76220b17914f56eAC0773047` | Proposal and voting engine; executes through the Timelock. Robust-quorum fix (pre-proposal denominator plus a floor and cap band). | No |
| `AereGovernorV2Timelock` | `0x9d618c5AB5f40c7187fc8D644d1aC9Bae0805f85` | 2-day delay queue; the only address a future migration would grant power to; Governor is PROPOSER, EXECUTOR, CANCELLER; deployer admin renounced. | No (owns nothing today) |
| `AereGovLockV2` (govAERE) | `0x547F58B087BEbB1746d6159070E755b868C27731` | Custody-lock, non-transferable, checkpointed vote source; voting power cannot detach from capital. | No |
Live governance parameters on the deployed Governor: proposal threshold 100,000 govAERE, voting delay about 1 day, voting period about 7 days, quorum 10% of past total supply, simple-majority pass rule, Timelock delay exactly 2 days (timestamp based). The Timelock separates three powers: PROPOSER (queue), EXECUTOR (run), and CANCELLER (cancel a queued item), which is the role structure the threshold design in Section 3.2 attaches to.
Provenance and lineage (retained for the record, not migration targets):
- Deprecated first stack: `AereGovernor 0xd16C2551Bf4f3d0DDF961282078917b2e8679999`, `AereTimelockController 0xf95738Cf366Dd2ea76fBa1b2736163BDc11D2e55`, `AereStakedGovVotes` (the original gAERE) `0xD7575795A055cb093957B86f0607871A2D37805C`. Carries a lazy-sync vote source and a quorum-inflation finding.
- Deprecated custody-lock stack: `AereGovLock 0xec7994D1Cf8C00b5674AEBd786b0680E56A8eeF8`, `AereGovLockGovernor 0xb7657b15bFa6369c84a431fc4e47f7f8234B39Cf`, `AereGovLockTimelock 0x87E1874f3d78925671a889EaBf64d2001f27dD81`. Superseded by the V2 stack above.
- Legacy advisory governor: `AereGovernanceStaked 0x8D77C888e439C4fADb2e23F1567a0A1965F80bCb`, structurally inert.
### 2.2 The governance token (gAERE / govAERE)
Voting power is a non-custodial, checkpointed mirror of real staking, never a new token (AERE is the only token).
- The original `gAERE`, `AereStakedGovVotes 0xD7575795A055cb093957B86f0607871A2D37805C`, is a checkpointed `ERC20Votes` adapter over `AereStakingV2` (`0x1D95eF6D17aeAB732dF914Ba2d018c270BC155FC`) and `AereLockedStaking` (`0x21108c28A849b05aE6b7a3a5bc435C9Bc897E7Ad`). It custodies nothing, is non-transferable, and is minted or burned by a permissionless `sync`. It is superseded together with the deprecated stack it served.
- The canonical vote source is `govAERE`, `AereGovLockV2 0x547F58B087BEbB1746d6159070E755b868C27731`, a custody-lock veToken-style non-transferable checkpointed votes source paired with `AereGovernorV2`.
Both expose the `IVotes` interface that OpenZeppelin's Governor reads via `getPastVotes(account, snapshotBlock)`, so a vote's weight is fixed at a past snapshot and is flash-stake resistant. Live supply is 0 today because live stake is 0.
### 2.3 The post-quantum threshold account
`AereThresholdAccount` (source `aerenew/contracts/contracts/mpc/AereThresholdAccount.sol`) is a non-custodial ERC-4337 v0.7 smart account whose owner is a t-of-n post-quantum committee. A call is authorized only when at least `threshold` DISTINCT committee members each supply a valid NIST post-quantum signature (Falcon-512 or Falcon-1024 or ML-DSA-44 or SLH-DSA-128s) over the account's domain-separated challenge, every leg verified in full, on-chain, through the live precompiles. No single party's key can move funds; there is no admin, owner override, or recovery backdoor. It exposes two domain-separated paths: an ERC-4337 `validateUserOp` path (bundler), and a direct `executeThreshold(target, value, data, legs)` self-relay path with its own `execNonce` replay barrier. Each leg is verified through the shared library `AerePQCThreshold`, whose scheme-to-precompile routing is `1 = Falcon-512 (0x0AE1)`, `2 = Falcon-1024 (0x0AE2)`, `3 = ML-DSA-44 (0x0AE3)`, `4 = SLH-DSA-128s (0x0AE4)`.
Deployment status of the threshold family:
| Component | Address | Status |
| --- | --- | --- |
| `AereThresholdAccountFactory` (CREATE2 factory for the account) | `0xf8fCC337d7749Ff2935D755C5F3934e2B70779E8` | LIVE on mainnet, but carries a duplicate-committee-key finding; 0 accounts ever created (zero exposure). Superseded by a source-only fix. |
| Account factory V2 (fixed, rejects duplicate keys) | not on mainnet | Source-fixed; mainnet redeploy is founder-gated. `[VERIFY: a session note recorded a V2 factory at 0x69734E4044B1C5943B9256A73De41B101BFA2633, but it was not independently confirmed on-chain and is treated here as not deployed. Confirm before citing as live.]` |
| `AereThresholdPQCRegistry` (registry-style t-of-n PQC authorization, buggy) | `0x9a6096F6FB3a7E54cF70DE12Cb3903Ae79D1C213` | LIVE, same duplicate-key finding, 0 committees (zero exposure). Superseded. |
| `AereThresholdPQCRegistry` V2 (fixed) | `0x8Fbfe1C72E8c83ca2a0c475ce3648D47bCC8643c` | Deployed and live on mainnet, inert (0 committees). |
| `AereThresholdRegistry` (classical threshold-ECDSA sibling, clean) | `0x875BA0dbA1806Ad9aE57627d705ecA12139D1EF4` | LIVE, no fund custody, 0 committees. Metadata plus `ecrecover`, not post-quantum. |
The registry V2 (`0x8Fbfe1...`) proves the t-of-n Falcon verification path end to end on live precompiles today. The account factory that would mint a control-bearing threshold account with the duplicate-key fix is not yet on mainnet; its redeploy is founder-gated (see Section 4).
### 2.4 The Falcon precompile and the Falcon-owned account
- Falcon-512 verification precompile: `0x0AE1`, live on mainnet since block 9,189,161, marginal verify cost 40,000 gas, fail-closed (returns a 32-byte zero word on a bad or malformed signature; never reverts). This is the single primitive every part of the design below leans on.
- `AerePQCAccount`: an ERC-4337 v0.7 smart account whose sole owner is a Falcon-512 public key (897 bytes, no classical ECDSA fallback), with `validateUserOp` and EIP-1271 `isValidSignature` both decided by Falcon. The live sample account is `AerePQCAccount_sample 0xa42a5e7F72E46BadC11367650Ec34D676194326f`; its EIP-1271 path was demonstrated live (accept magic `0x1626ba7e`, tampered signature to `0x0`). Honest cost note: the live sample decides `isValidSignature` through the pure-Solidity `AereFalcon512Verifier 0x4E8e9682329e646784fB3bd01430aA4bA54D8fFC` at about 10.5M gas per verify; a precompile-backed `isValidSignature` (routing to `0x0AE1`, 40,000 gas) is the variant the vote path in Section 3.1 should use.
- `AerePQCAttestation 0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A` demonstrates the full pattern already: a contract that takes a NIST post-quantum public key and a signature and verifies, on-chain, through the live precompile, that the signature covers a domain-separated challenge, with strict nonce replay protection. The vote path reuses this exact shape.
## 3. The design
Three parts. Each states the mechanism, then whether it is a capability on the primitives above or new contract work.
### 3.1 Part A: post-quantum-signed votes
**Goal.** A voter authenticates a ballot with a Falcon-512 signature, verified by `0x0AE1`, instead of or in addition to an ECDSA signature, so that a decisive vote cannot be forged by breaking secp256k1 alone.
OpenZeppelin's Governor already exposes two authenticated vote entry points: `castVote` (authenticated by `msg.sender`) and `castVoteBySig` (authenticated by an EIP-712 signature over the ballot, so a vote can be relayed by a third party). `castVoteBySig` resolves the signature through `SignatureChecker.isValidSignatureNow(voter, hash, signature)`, which accepts EITHER an ECDSA signature from an EOA voter OR, when `voter` is a contract, an EIP-1271 `isValidSignature` result from that contract.
There are two mechanisms, at two different levels of effort.
**Mechanism A1 (EIP-1271 path, capability on existing primitives).** Let the voter be a Falcon-owned smart account (`AerePQCAccount`, or an `AereThresholdAccount` for a committee voter). The voter delegates its govAERE voting weight to itself in the standard `ERC20Votes` way. A relayer submits `castVoteBySig(proposalId, support, voterAccount, falconBallotSignature)`. The unmodified Governor calls `voterAccount.isValidSignature(ballotHash, falconBallotSignature)`, the account verifies the Falcon signature through `0x0AE1`, and returns the EIP-1271 magic value on success. The Governor counts the vote. No Governor change is required, because the standard `castVoteBySig` plus EIP-1271 path already routes signature authority to the voter's own account. This is the important honesty point for Part A: a Falcon-authenticated ballot is expressible today, on the deployed Governor, using an `AerePQCAccount` voter, provided that account's `isValidSignature` is backed by the `0x0AE1` precompile (40,000 gas) rather than the ~10.5M-gas Solidity verifier the live sample uses. Recommended concrete step: deploy a precompile-backed `AerePQCAccount` variant and use it as the voter.
**Mechanism A2 (explicit-verify path, new contract work).** For a voter that has no deployed smart account (a raw Falcon keypair), add a Governor extension, `AerePQCGovernor`, with an explicit ballot entry point:
```
castVoteWithFalconSig(
uint256 proposalId,
uint8 support,
address voter, // the govAERE-weighted identity
bytes falconPubKey, // 897-byte Falcon-512 public key bound to `voter`
bytes falconSignature // Falcon signature over the domain-separated ballot
)
```
The extension recomputes the ballot challenge `keccak256(BALLOT_DOMAIN, block.chainid, address(this), proposalId, support, voter, nonce)`, calls `0x0AE1` with `(falconPubKey, challenge, falconSignature)`, and on a verified result records the vote with weight `getPastVotes(voter, proposalSnapshot)`. The public-key-to-`voter` binding is established once through a proof-of-possession registration, exactly as `AerePQCAttestation` and `AerePQCKeyRegistry 0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691` already do on live precompiles, so a voter cannot claim a Falcon key it does not control. A strictly increasing per-voter ballot nonce prevents replay. This path is a new, small, audit-scoped extension of the OpenZeppelin counting module; it does not touch the vote-weight source or the Timelock.
Either mechanism can be offered as an additional path alongside ECDSA rather than a replacement, so classical and post-quantum voters coexist during a transition.
### 3.2 Part B: post-quantum threshold timelock control
**Goal.** The multisig approval that guards queued upgrades is a t-of-n Falcon committee, not a classical ECDSA multisig, so the control path over the Timelock is post-quantum.
In the classical governance design (`GOVERNANCE.md`, Section 6.2) the Timelock's safety valve is a guardian multisig that holds only the CANCELLER role: it can veto a queued proposal during the 2-day delay, but it cannot propose, execute, or change parameters. In a conventional deployment that guardian is a Gnosis-Safe-style ECDSA multisig. The design replaces it with an `AereThresholdAccount`.
**Mechanism.** Because `AereThresholdAccount` is an ERC-4337 account that can make an arbitrary authorized call through `executeThreshold(target, value, data, legs)`, and because the OpenZeppelin `TimelockController` grants and checks roles by address, the threshold account can hold a Timelock role directly:
- Grant the CANCELLER role on `AereGovernorV2Timelock 0x9d618c5A...5f85` to an `AereThresholdAccount` whose committee is the guardian set. A veto then requires at least `threshold` distinct guardians to each Falcon-sign the exact `cancel(id)` challenge; the account verifies each leg through `0x0AE1` and only then issues the cancel. The veto control path is now post-quantum and t-of-n, with no single guardian key able to veto alone and no ECDSA key on the safety valve.
- Optionally, the same construction extends to PROPOSER and EXECUTOR. A committee-gated PROPOSER means queuing a Timelock action requires a Falcon t-of-n approval; a committee-gated EXECUTOR means running a matured action does too. Least-authority still applies: grant only the roles the deployment intends the committee to hold.
The distinctness guarantee is load-bearing here, which is exactly why the duplicate-key finding matters: a committee that accepted the same public key at two indices could let one keyholder fill multiple slots and reach threshold alone, collapsing the t-of-n property. The fixed account source rejects duplicate committee keys at initialization, and the fixed PQC registry (`0x8Fbfe1...`) proves the corrected verification path on live precompiles. This is why a control-bearing threshold guardian must wait for the founder-gated account-factory V2 redeploy (Section 4), not the live V1 factory.
### 3.3 Part C: post-quantum-authorized upgrades end to end
**Goal.** A parameter change or contract upgrade is authorized by a post-quantum-signed governance action from the ballot to the Timelock-gated execution, with no classical-only choke point on the control path.
**Mechanism.** Compose Parts A and B along the standard proposal lifecycle (propose, voting delay, active voting, queue, Timelock delay, execute), replacing the classical authentication at each authority-bearing step with a post-quantum one:
1. **Propose.** A proposer whose snapshot govAERE weight meets the 100,000 threshold submits `(targets[], values[], calldatas[], description)`. If the proposer is a Falcon-owned account, the proposal transaction itself is authorized post-quantum (Part A account as the sender).
2. **Vote.** Holders cast Falcon-signed ballots via Mechanism A1 or A2. A proposal succeeds on quorum plus simple majority, read from the past snapshot.
3. **Queue.** The succeeded proposal is queued into `AereGovernorV2Timelock`. If PROPOSER is the threshold committee (Part B), queuing requires a Falcon t-of-n approval.
4. **Timelock delay and veto.** During the 2-day delay, the post-quantum guardian committee (Part B, CANCELLER) can veto with a Falcon t-of-n signature. The delay is timestamp based and exact regardless of block time.
5. **Execute.** After the delay, execution makes the calls: a `transferOwnership`, a parameter setter, a treasury spend, or an upgrade call. If EXECUTOR is the threshold committee, execution requires a Falcon t-of-n approval too.
The end-to-end property is that every authority-bearing decision on the path, who may vote, whether the vote counted, who may queue, who may veto, who may execute, rests on a NIST post-quantum signature verified by the live `0x0AE1` precompile, not on an ECDSA signature alone. The vote-weight source (govAERE) and the delay semantics are unchanged; only the authentication is upgraded. What an upgrade can reach is unchanged from the classical framework: Ownable protocol contracts whose ownership the Foundation migrates to the Timelock are governable; immutable, ownerless contracts (`AereSink 0x69581B86A48161b067Ff4E01544780625B231676`, the post-quantum verifier suite, the state-channel and rollup-validity contracts) remain permanently outside governance and cannot be reached by any proposal, post-quantum-signed or not.
## 4. Honest status and gates
### 4.1 Capability on existing primitives versus new contract work
| Element | Classification | Basis |
| --- | --- | --- |
| Falcon-512 verification of a governance signature | Capability, live | `0x0AE1` live since block 9,189,161; used in production by `AerePQCAttestation`, `AerePQCKeyRegistry`, `AereThresholdPQCRegistry` V2. |
| Falcon-authenticated ballot via EIP-1271 voter (A1) | Capability, on existing primitives | Unmodified OpenZeppelin `castVoteBySig` plus EIP-1271 already routes signature authority to the voter account; needs a precompile-backed `AerePQCAccount` voter, no Governor change. |
| Explicit `castVoteWithFalconSig` extension (A2) | New contract work | A new, small `AerePQCGovernor` counting extension plus a proof-of-possession key binding. Audit-scoped. |
| t-of-n Falcon committee holding a Timelock role (B) | Capability in principle, gated in practice | `AereThresholdAccount` exists and its verification path is proven; a control-bearing account needs the founder-gated factory V2 redeploy (below). Wiring is a role grant on the Timelock. |
| End-to-end post-quantum-authorized upgrade (C) | Composition of the above | No new consensus or protocol change; a composition of A, B, and the standard lifecycle. |
### 4.2 Governance is inert today
Nothing in this design is live governance. The Governor and Timelock own nothing and control nothing until an audit-gated ownership transfer, live voting power is zero because no AERE is staked, and the Foundation key (a single-key EOA, not yet a multisig) still owns effectively every Ownable protocol contract across 7 Foundation-operated QBFT validators. A passed proposal, classical or post-quantum-signed, changes nothing about the protocol until stake exists and ownership is migrated. This design does not change that status; it specifies the authentication the governance layer should use if and when it is activated.
### 4.3 Cost note
Post-quantum verification is more expensive than ECDSA, and the design accepts that cost because governance actions are low-frequency. The marginal verify cost, measured on the live precompiles, relative to `ecrecover` at 3,000 gas:
| Operation | Marginal verify gas | Ratio to ecrecover |
| --- | --- | --- |
| `ecrecover` (secp256k1) | 3,000 | 1x |
| Falcon-512 (`0x0AE1`) | 40,000 | about 13x |
| ML-DSA-44 (`0x0AE3`) | 55,000 | about 18x |
| Falcon-1024 (`0x0AE2`) | 75,000 | about 25x |
| SLH-DSA-SHA2-128s (`0x0AE4`) | 350,000 | about 115x |
So a post-quantum verify costs roughly 13x to 115x an ECDSA recovery depending on scheme, and a Falcon-512-signed vote or veto carries the 40,000-gas leg (about 13x) on top of the ordinary vote or cancel transaction. Every figure is well under the EIP-7825 per-transaction cap of 2^24 (16,777,216) gas, and a t-of-n veto pays that leg once per participating member. For a vote cast once per proposal and a veto used rarely, this is acceptable, but it is a real and stated cost, not a free upgrade. It should not be applied to a high-frequency hot path; governance is precisely the low-frequency, high-value setting where it is worth paying.
### 4.4 Activation is founder-gated and audit-gated
Three independent gates, in order, exactly as the classical governance framework already commits to:
1. **External audit (blocking).** The Governor, Timelock, and vote source, plus any new `AerePQCGovernor` extension and the threshold-account factory V2, must be reviewed by an external auditor before the Timelock owns anything of value.
2. **Real, distributed stake.** Quorum and threshold are meaningful only once real AERE is staked and delegated, and only once operation decentralizes beyond a single operator.
3. **Foundation migrates ownership (staged).** For each contract to be governed, the Foundation key calls `transferOwnership(Timelock)`. This design performs no such transfer.
Two post-quantum-specific gates sit inside these:
- The control-bearing `AereThresholdAccount` for Part B requires the founder-gated account-factory V2 redeploy. The current live factory (`0xf8fCC337...`) carries the duplicate-key finding; the fix is applied to source but its mainnet redeploy has not occurred and is founder-gated. A t-of-n governance guardian must be minted by the fixed factory, not the live one. `[VERIFY: confirm the account-factory V2 mainnet address once the founder-gated redeploy is executed; no confirmed on-chain V2 factory address exists at the time of writing.]`
- The A1 vote path should use a precompile-backed `AerePQCAccount` variant (40,000 gas) rather than the live sample's Solidity verifier (~10.5M gas). `[VERIFY: confirm the deployed address of the precompile-backed AerePQCAccount variant used as the vote identity; the live sample 0xa42a5e...326f uses the Solidity verifier path.]`
## 5. Scope boundary
This is governance-layer post-quantum authentication and nothing more.
- It does NOT make Aere consensus post-quantum. Consensus stays Hyperledger Besu QBFT with classical secp256k1 ECDSA, N=7 validators, f=2, commit quorum 5 of 7, and roughly 0.5-second blocks. Validator identity is by ECDSA `ecrecover` over committed seals; that is unchanged.
- The precompiles this design uses are application-layer and account-layer. A contract verifying a Falcon signature is not the chain reaching post-quantum consensus, and this document must never be read as claiming otherwise.
- Post-quantum-authenticated governance can decide, subject to audit and founder gating, to pursue post-quantum consensus as a separate, higher-risk program. That is a different and much larger change (a supervised consensus activation, not a contract deployment), out of scope here.
- Post-quantum authentication of a governance signature does not make the interop and zk light-client contracts post-quantum: those still rely on classical secp256k1 seals and Groth16 over BN254, and are trust-minimized, not trustless. This design does not touch them.
The one-sentence statement of scope: this specifies how governance authenticates itself with post-quantum signatures on already-live precompiles, as an audit-gated and founder-gated design over an inert governance layer, and it changes nothing about consensus.
## Appendix: cited addresses (chain 2800)
| Name | Address | Notes |
| --- | --- | --- |
| Falcon-512 precompile | `0x0AE1` | Live since block 9,189,161; 40,000 gas; fail-closed. |
| Falcon-1024 / ML-DSA-44 / SLH-DSA-128s / SHAKE256 precompiles | `0x0AE2` / `0x0AE3` / `0x0AE4` / `0x0AE5` | Live, same band. |
| `AereGovernorV2` | `0x03251BD9A115385B76220b17914f56eAC0773047` | Canonical Governor, inert. |
| `AereGovernorV2Timelock` | `0x9d618c5AB5f40c7187fc8D644d1aC9Bae0805f85` | Canonical Timelock, owns nothing. |
| `AereGovLockV2` (govAERE) | `0x547F58B087BEbB1746d6159070E755b868C27731` | Canonical vote source. |
| `AereStakedGovVotes` (original gAERE) | `0xD7575795A055cb093957B86f0607871A2D37805C` | Deprecated ERC20Votes adapter. |
| `AereThresholdAccountFactory` (live V1) | `0xf8fCC337d7749Ff2935D755C5F3934e2B70779E8` | Duplicate-key finding; 0 accounts; superseded. |
| Account factory V2 (fixed) | not on mainnet | Founder-gated redeploy. `[VERIFY: 0x69734E4044B1C5943B9256A73De41B101BFA2633 recorded in a session note, not confirmed on-chain; treated as not deployed.]` |
| `AereThresholdPQCRegistry` V1 (buggy) | `0x9a6096F6FB3a7E54cF70DE12Cb3903Ae79D1C213` | 0 committees; superseded. |
| `AereThresholdPQCRegistry` V2 (fixed) | `0x8Fbfe1C72E8c83ca2a0c475ce3648D47bCC8643c` | Deployed and live, inert. |
| `AereThresholdRegistry` (ECDSA sibling) | `0x875BA0dbA1806Ad9aE57627d705ecA12139D1EF4` | Classical threshold-ECDSA, not post-quantum. |
| `AerePQCAccount_sample` | `0xa42a5e7F72E46BadC11367650Ec34D676194326f` | Falcon-512-owned; Solidity-verifier EIP-1271 path. |
| `AereFalcon512Verifier` (Solidity) | `0x4E8e9682329e646784fB3bd01430aA4bA54D8fFC` | ~10.5M-gas view verifier; the precompile is the cheap path. |
| `AerePQCKeyRegistry` | `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691` | Proof-of-possession key binding on live precompiles. |
| `AerePQCAttestation` | `0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A` | Reference pattern: on-chain precompile-verified, domain-separated, replay-protected. |
| `AereGovernorV2Timelock` guardian (design) | to be an `AereThresholdAccount` | Minted by the fixed factory V2 once founder-gated redeploy occurs. |
Source files: `aerenew/contracts/contracts/mpc/AereThresholdAccount.sol`, `aerenew/contracts/contracts/mpc/AerePQCThreshold.sol`, `aerenew/docs/GOVERNANCE.md`, `aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md`, `aerenew/docs/AUDIT-FINDING-THRESHOLD-DUPLICATE-KEYS-2026-07-15.md`, `aerenew/sdk-js/src/addresses.ts`.

View File

@ -0,0 +1,57 @@
# Spec 6: AerePQAggregate build summary
## What was built
Constant-cost on-chain verification of a t-of-n POST-QUANTUM committee authorization via ONE
succinct SP1 proof, exposed as an ERC-4337 path and a portable ERC-7579 validator module.
### Files (new, no deployed contracts modified)
- `aerenew/contracts/contracts/mpc/AerePQAggregateVerifier.sol`
- `registerCommittee(root, t, n, scheme)` — stores committee (root = commitment to n PQC keys;
keys NOT stored on-chain, so cost stays O(1) in n). Permissionless, no admin, no funds.
- `verifyAggregate(committeeId, messageDigest, publicValues, proofBytes)` view — strict fail-closed:
binds committeeRoot / t / n / scheme / messageDigest / validCount>=t, then ONE
`SP1_GATEWAY.verifyProof(pinnedVKey, publicValues, proofBytes)`. Reverts on any mismatch or bad proof.
- `tryVerifyAggregate(...)` view — non-reverting bool (try/catch wrap) for 4337/7579 SHOULD-not-revert.
- `recordAggregate(...)` — strict verify + provenance attestation event.
- Immutable `SP1_GATEWAY` (chain 2800 canonical Succinct gateway 0x9ca479C8…70628) and
`AGGREGATE_PROGRAM_VKEY` (pinned circuit; registrant cannot swap circuits).
- Public-values layout (fixed 192 bytes / 6 words):
`abi.encode(bytes32 committeeRoot, uint256 t, uint256 n, uint256 scheme, bytes32 msgDigest, uint256 validCount)`.
- `aerenew/contracts/contracts/modular/AerePQAggregateModule.sol`
- ERC-7579 type-1 validator (matches OZ draft-IERC7579 `IERC7579Validator`): `onInstall`/`onUninstall`/
`isModuleType`, `validateUserOp(PackedUserOperation, bytes32)->uint256` (0/1), and
`isValidSignatureWithSender(address,bytes32,bytes)->bytes4` (0x1626ba7e / 0xffffffff).
- Singleton, per-account committee binding keyed by msg.sender (AereSessionKeyValidator pattern).
- Signature layout: `abi.encode(bytes publicValues, bytes proofBytes)`; malformed => FAILED, no revert.
- Local PackedUserOperation struct (self-contained, portable to any EVM chain).
- `aerenew/contracts/test/AerePQAggregate.test.js` — 17 tests.
- `aerenew/docs/AERE-PQ-AGGREGATE.md` — scheme, gas argument, 4337/7579 portability, Bank28 use case,
honest [MEASURE] status.
## REAL test result
`npx hardhat test test/AerePQAggregate.test.js` => **17 passing (2s)**. Ran from aerenew/contracts.
Test double = MockSp1Verifier (verifyProof REVERTS on invalid proof, exact production gateway semantics),
same double as AereComputeMarketV3 / zk light clients.
Coverage: valid proof authorizes (view+recorded); invalid proof rejects fail-closed; wrong message
digest rejects; wrong committee root rejects; below-threshold rejects; t/n/scheme mismatch + malformed
public values reject; unknown committee rejects; module returns 4337 code + ERC-1271 magic correctly and
fails closed without reverting on invalid/malformed.
### Gas independence (the headline)
`recordAggregate`: n=5 => 61,799 gas; n=1,000,000 => 61,823 gas; diff 24 gas (calldata encoding of the
larger integer, O(log n) not O(n)). Concretely proves constant on-chain cost regardless of committee size.
## Flags
- [MEASURE] OFF-CHAIN SP1 aggregation guest circuit NOT implemented (research zkVM work). Verifying
Falcon FFT/float sampling in a zkVM is hard => ML-DSA-65 (FIPS 204, integer arithmetic) is the tractable
first target (SCHEME_MLDSA65 = 5). On-chain side is built + tested vs a MOCK gateway only. Do NOT claim
end-to-end aggregation works.
- [MEASURE] real-gateway on-chain verify gas + real proving cost/latency (mock numbers above).
- [VERIFY] ERC-7579 module assumes account stripped the validator-routing prefix before calling
(matches OZ AccountERC7579); PackedUserOperation field layout to reconcile vs Safe7579 / Kernel / Nexus.
- [VERIFY] isValidSignatureWithSender intentionally does NOT scope by `sender` (committee is sole authority).
## Scope boundary kept
Account/authorization aggregation only. Consensus stays classical ECDSA QBFT. No new token. No funds,
no admin. solc 0.8.23, viaIR, matches repo style. No em-dashes in prose.

210
AERE-PQ-AGGREGATE.md Normal file
View File

@ -0,0 +1,210 @@
# AerePQAggregate: constant-cost t-of-n post-quantum committee authorization
Status: on-chain verifier and ERC-7579 module BUILT and TESTED against a mock SP1 gateway.
Off-chain SP1 aggregation circuit NOT implemented, [MEASURE], research frontier.
Scope: account and authorization layer only. This changes nothing about Aere Network
consensus, which stays classical ECDSA QBFT. What is post-quantum here is the AUTHORITY
that approves a message, not the block production.
Contracts:
- `contracts/contracts/mpc/AerePQAggregateVerifier.sol`
- `contracts/contracts/modular/AerePQAggregateModule.sol`
- Test: `contracts/test/AerePQAggregate.test.js` (17 passing)
---
## 1. The problem this answers
NIST post-quantum signatures are large. An ML-DSA-65 signature is about 3,300 bytes, an
ML-DSA-44 signature about 2,420 bytes, an SLH-DSA-128s signature about 7,856 bytes. A t-of-n
committee that authorizes on-chain by supplying t independent PQC signatures therefore pays,
on-chain, for t signature verifications plus t large calldata blobs. That cost is LINEAR in t
and, under a per-transaction gas ceiling (Aere Network keeps Fusaka's EIP-7825 cap of 2^24 gas),
it bounds how large a committee can practically authorize in one transaction. This is the same
economic wall that motivates EIP-7932 (transaction-level signature abstraction for large or
alternative signature schemes): post-quantum signatures do not fit the classical fixed-size
signature budget.
`AereThresholdPQCRegistry` (already in this repo) takes the honest linear path: it verifies t
INDEPENDENT PQC signatures on-chain through Aere Network's live precompiles. Every leg is a real
NIST PQC signature, the t-of-n participation is proven on-chain, and the cost is linear in t.
`AerePQAggregate` takes the succinct path. It moves the t (up to n) signature verifications OFF
the chain, into a zero-knowledge virtual machine (the SP1 zkVM), and verifies ONE small proof
on-chain. The on-chain cost becomes INDEPENDENT of the committee size n.
---
## 2. The scheme
### Off-chain (the SP1 aggregation circuit, [MEASURE], NOT implemented here)
A relying party commits, once, to a committee: a `root` (a binding commitment to the exact n
authorized PQC public keys, for example a Merkle root over the canonically ordered keys or the
keccak256 of the canonical key list), a threshold `t`, a size `n`, and a `scheme`.
To authorize a 32-byte `messageDigest`, an off-chain prover runs the aggregation guest program
inside the SP1 zkVM with:
- PRIVATE witness: the n committed public keys, and at least t PQC signatures over `messageDigest`.
- The circuit MUST, inside the zkVM:
1. recompute `root` from the witnessed keys (and abort if it does not match the committed root),
2. verify each of the counted signatures against a DISTINCT committed key over `messageDigest`,
using the scheme's real verification algorithm,
3. never double-count a key (distinctness by committed index),
4. output the PUBLIC values below.
- PUBLIC output (the fixed 192-byte layout the on-chain verifier decodes):
`abi.encode(bytes32 committeeRoot, uint256 threshold, uint256 committeeSize, uint256 scheme,
bytes32 messageDigest, uint256 validCount)`
where `validCount` is the number of distinct valid signatures the circuit verified, and must be
at least `threshold`.
The proof is succinct: its size and its on-chain verification cost are fixed by the SP1 verifier,
independent of n and of how many signatures the circuit checked internally.
### On-chain (BUILT and TESTED here)
`AerePQAggregateVerifier`:
1. `registerCommittee(root, t, n, scheme)` stores the committee. The n public keys are NOT stored
on-chain (storing them would reintroduce the O(n) cost the design removes); `root` is the sole
on-chain commitment to them. Permissionless, no admin, holds no funds.
2. `verifyAggregate(committeeId, messageDigest, publicValues, proofBytes)` binds the proof's public
values to the registered committee and to the caller's claimed message, then makes a SINGLE
`verifyProof` call to the deployed SP1 verifier gateway. It is fail-closed: it rejects unless
ALL hold:
- the committee is registered,
- `publicValues.committeeRoot == committee.root` (the sole authority binding),
- `publicValues.threshold / committeeSize / scheme == committee.{threshold,size,scheme}`
(defense in depth),
- `publicValues.messageDigest == messageDigest` (the caller's claimed message),
- `publicValues.validCount >= committee.threshold`,
- the SP1 proof verifies against the pinned circuit vkey (`verifyProof` REVERTS on an invalid
proof).
3. `tryVerifyAggregate(...)` is the non-reverting variant (returns bool) used by the ERC-4337 and
ERC-7579 validation paths, which SHOULD return a failure code rather than revert.
4. `recordAggregate(...)` verifies (strict) and records an on-chain provenance attestation.
The circuit is pinned. `AGGREGATE_PROGRAM_VKEY` is fixed at construction, so a registrant cannot
substitute a different circuit: every committee is proven by the same audited program through the
same immutable gateway. Deploy one verifier per circuit (for example one for the ML-DSA-65
aggregation program).
The deployed SP1 verifier gateway on Aere Network mainnet (chain 2800) is the canonical Succinct
gateway at `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`, the same gateway the parallel-execution
validity contracts and the zk light clients already call.
---
## 3. The gas argument: constant vs linear in n
The verifier does NO per-member work on-chain. There is no loop over keys, no per-signature
precompile call, no per-member calldata. It reads a fixed committee record, compares six words of
public values, and makes one `verifyProof` call. The cost is therefore fixed by the SP1 verifier
and independent of n.
Measured in the test suite (`recordAggregate`, mock gateway):
| committee size n | gas used |
| ---------------- | -------- |
| 5 | 61,799 |
| 1,000,000 | 61,823 |
n grew by 200,000x; gas moved by 24 (the calldata encoding of the larger integer, which is
O(log n), not O(n)). A linear on-chain t-of-n PQC verifier pays tens of thousands of gas PER
signature (the marginal precompile verify cost for ML-DSA-44 is about 55,000 gas, before calldata),
so at even a modest committee it hits the per-transaction gas ceiling; the aggregate path does not.
(The gas figures above are for the mock gateway. The REAL SP1 Groth16/Plonk verifier is more
expensive per call but still CONSTANT in n; the constant-vs-linear conclusion is what the on-chain
design guarantees. The absolute real-gateway number is [MEASURE].)
---
## 4. ERC-4337 and ERC-7579 portability
The authorization is exposed two ways.
- ERC-4337 authorization path. `messageDigest` is the userOpHash, so the aggregate proof is bound
to one exact operation. The account nonce inside the userOpHash makes the authorization
non-replayable at the account layer, so the verifier itself needs no separate replay nonce.
- ERC-7579 validator module. `AerePQAggregateModule` implements the type-1 validator surface from
the ERC-7579 standard (OpenZeppelin `interfaces/draft-IERC7579.sol` `IERC7579Validator`):
`onInstall` / `onUninstall` / `isModuleType`, `validateUserOp(PackedUserOperation, bytes32)`
returning the ERC-4337 code (0 success, 1 failure), and `isValidSignatureWithSender(address,
bytes32, bytes)` returning the ERC-1271 magic value `0x1626ba7e` on success. An account is bound
to a committee at install time; the module then validates each userOp (and each ERC-1271
signature) by asking the verifier whether the committee signed the digest. Both paths are
fail-closed and never revert on a bad or malformed signature, per the validator SHOULD.
Because ERC-7579 is a chain-agnostic modular-account standard, the SAME module can be installed on
a Safe (via the Safe7579 adapter), a Kernel, or a Nexus account on ANY EVM chain, and the verifier
it points at can be deployed on that chain against that chain's SP1 gateway. The rule "a t-of-n
post-quantum committee approved this userOp" then travels with the account across chains.
[VERIFY] The module assumes the account has already stripped any validator-routing prefix (ERC-7579
stacks select the validator by an address carried in the userOp nonce key, or in some stacks a
prefix on the signature) before calling the module, matching the OpenZeppelin `AccountERC7579`
dispatcher. Confirm the exact unwrapping and the PackedUserOperation field layout against the
specific account implementation you deploy under (Safe7579 / Kernel / Nexus).
---
## 5. Bank28 custody use case
Bank28 is a non-custodial neobank stack. A treasury or custody account can be governed by a t-of-n
committee of post-quantum keys (for example 3-of-5 ML-DSA-65 signers held on separate HSMs or
devices). With `AereThresholdPQCRegistry` the account pays on-chain for t independent PQC
verifications each time it moves funds, and the committee size is bounded by the per-transaction
gas ceiling. With `AerePQAggregate`:
- the committee can be large (institutional custody often wants a big quorum: many independent
approvers, several of them offline) because on-chain cost does not grow with n,
- each fund movement is one small proof, so gas and calldata are predictable and flat,
- the account is a standard ERC-7579 modular account with the validator module installed, so it
works with existing bundler and wallet infrastructure and is portable across the chains Bank28
settles on,
- the authority is quantum-durable: a quantum adversary that can forge ECDSA still cannot authorize
a movement without the committee's PQC private keys, because the underlying legs are NIST PQC
signatures proven in the zkVM.
Scope note: this authorizes ACCOUNT actions. It is not custody advice and it does not make Aere
Network consensus post-quantum. Bank28 must still follow its lawful KYC/AML obligations; this is an
authorization primitive, not a compliance control.
---
## 6. Honest status
BUILT and TESTED (this repo):
- `AerePQAggregateVerifier.sol`: committee registry, public-input binding scheme, fail-closed
strict and non-reverting verification, constant-cost single `verifyProof` call, provenance
recording.
- `AerePQAggregateModule.sol`: ERC-7579 type-1 validator module wrapping the verifier for the
ERC-4337 and ERC-1271 paths.
- 17 passing Hardhat tests against `MockSp1Verifier`, the same deployed-gateway test double used by
`AereComputeMarketV3` and the zk light clients. Its `verifyProof` REVERTS on an invalid proof,
so the fail-closed path is exercised exactly as against the production gateway. Tests cover: a
valid proof authorizes; an invalid proof rejects fail-closed; a wrong-message-digest proof
rejects; a wrong-committee-root proof rejects; below-threshold rejects; threshold/size/scheme
mismatch and malformed public values reject; unknown committee rejects; gas is constant across
n = 5 vs n = 1,000,000; and the module returns the correct ERC-4337 code and ERC-1271 magic value
on valid inputs and fails closed (without reverting) on invalid or malformed ones.
NOT implemented, [MEASURE], research frontier:
- The OFF-CHAIN SP1 aggregation guest circuit. Verifying n PQC signatures against the committee root
inside a zkVM and emitting the public values is real cryptographic engineering that is NOT in this
repository. The on-chain verifier is proven against a mock gateway that asserts the bound public
values; a real end-to-end system additionally needs the real circuit and a real SP1 proof.
- Verifying Falcon inside a zkVM is HARD: Falcon signature verification touches FFT over the ring
and, in reference implementations, floating-point Gaussian sampling paths, which are expensive and
delicate to make deterministic and constraint-friendly in a zkVM. ML-DSA (FIPS 204, Dilithium
lineage) is integer-arithmetic and modular-reduction based, with no floating point, so
ML-DSA-65 aggregation is the TRACTABLE FIRST TARGET. The pinned scheme constant in the verifier
reflects this (`SCHEME_MLDSA65 = 5`). A Falcon aggregation circuit is a later, separate verifier
deployment with its own pinned vkey.
- The real end-to-end proving cost and latency, and the real-gateway on-chain verify gas, are
[MEASURE] and depend on the circuit implementation.
Do NOT claim the aggregation works end-to-end. The claim that IS supported by this repository: the
on-chain verification and portability rail is built and fail-closed, and its cost is provably
constant in the committee size n.

View File

@ -0,0 +1,56 @@
# Spec 4: Aere Network Post-Quantum Finality Certificate
Built the flagship ADDITIVE post-quantum finality feature: one succinct proof that a quorum of
Aere validators attested to a finalized block with HASH-BASED (post-quantum) keys, verifiable
on-chain in ONE call, WITHOUT changing QBFT consensus and WITHOUT a re-genesis.
## Deliverables (all new, no deployed contract modified)
1. `aerenew/contracts/contracts/pqfinality/AerePQAttestationKeyRegistry.sol`
- Per-validator hash-based attestation public key (XMSS / WOTS+ / leanSig / SLH-DSA / XMSS^MT),
SEPARATE from the ECDSA consensus key. This is the PQ finality key.
- Maps validator -> current key + monotonic keyEpoch. One-time/few-time keys rotate: registering
again appends a new record, marks predecessor superseded, bumps epoch. Append-only history,
fail-closed, no silent rewrite.
- Governed membership (Ownable enrollValidator = the N=7 set + root ordering), self-custodied
keys (validator registers its OWN key; owner cannot forge it).
- Deterministic `validatorSetRoot()` = keccak over (validator, keyEpoch, keyHash) leaves in
enrollment order. Any rotation changes the root.
2. `aerenew/contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol`
- Verifies ONE SP1 proof via the live gateway (0x9ca479C8...70628), pinned PROGRAM_VKEY.
- Public values (192 bytes): blockHash, blockHeight, validatorSetRoot, validatorSetSize,
quorumCount, attestationDomain.
- Fail-closed: binds root+size to the LIVE registry, binds attestationDomain (chainId+registry),
requires quorumCount >= ceil(2N/3) = 5 of 7, rejects zero block, then one verifyProof (reverts
on bad proof). Cost independent of N.
- `isFinalPQ(blockHash)` consumer gate + `recordCertificate` + `assertFinalizes(claimedBlock)`
for bridges confirming a specific block.
3. `aerenew/contracts/test/AerePQFinalityCertificate.test.js` - 12 passing (MockSp1Verifier).
Covers all required cases: 7-validator set registers (threshold 5); valid quorum-5 cert records +
isFinalPQ true; below-quorum rejects; wrong root rejects; wrong block/height/zero-block rejects;
invalid proof fail-closed; key rotation changes root and old-root cert no longer matches; empty
set fails closed; append-only + owner-gated enroll + self-register.
4. `aerenew/docs/AERE-PQ-FINALITY-CERTIFICATE.md` - architecture (dual-sign ECDSA+hash-based,
off-chain zkVM aggregates into one STARK/SP1 proof, on-chain one verify), why additive (no flip,
no re-genesis), quantum-safety argument (hash-based sigs + hash-based FRI STARK are Shor-
resistant vs classical ECDSA seals + BN254 Groth16), consumer use cases (light client/bridge/
L2/AI agent), live-first framing (ships ahead of Ethereum Lean Consensus ~2027 because N small),
honest [MEASURE] status.
## REAL test result
`npx hardhat test test/AerePQFinalityCertificate.test.js` from aerenew/contracts: 12 passing (2s),
0 failing. Contracts compile under solc 0.8.23 (repo config).
## Honesty flags
- [MEASURE] / NOT implemented: the off-chain XMSS/leanSig aggregation zkVM guest circuit (verify N
hash-based sigs against the root inside the zkVM, emit public values). Novel research. leanSig or
XMSS-in-zkVM is the target; ML-DSA-in-zkVM is a lattice alternative (loses the hash-only argument).
- On-chain verifier + registry are REAL and tested against a MOCK SP1 gateway. NOT proven end-to-end.
Do NOT claim PQ finality is live.
- [VERIFY]/design-only: the Shor-resistant end-to-end claim holds for a NATIVE hash-based STARK proof;
a Groth16 wrap would reintroduce a BN254 leg. Stated in the doc.
- Scope boundary kept: base QBFT consensus is STILL classical ECDSA. This is additive attestation ON
TOP; it never flips or gates consensus and is not live.

View File

@ -0,0 +1,285 @@
# Aere Network Post-Quantum Finality Certificate
Status: on-chain certificate verifier and attestation-key registry BUILT and TESTED against a
mock SP1 gateway. The off-chain XMSS / leanSig aggregation zkVM circuit is NOT implemented,
[MEASURE], and is novel research-frontier work.
Scope: ADDITIVE post-quantum finality attestation. This changes nothing about Aere Network
consensus, which stays classical ECDSA QBFT. Validators keep signing ECDSA committed seals for
consensus and ALSO sign a hash-based attestation over each finalized block. The certificate
proves the second, additive quorum. It does not replace, gate, or flip consensus, it needs no
re-genesis, and PQ finality is not live. Aere consensus is not post-quantum, and nothing here
claims it is.
Contracts:
- `contracts/contracts/pqfinality/AerePQAttestationKeyRegistry.sol`
- `contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol`
- Test: `contracts/test/AerePQFinalityCertificate.test.js` (12 passing)
---
## 1. What a Post-Quantum Finality Certificate is
A Post-Quantum Finality Certificate is ONE succinct proof that a quorum of Aere Network
validators attested to a finalized block using their HASH-BASED (post-quantum) attestation keys.
Anyone, a light client, a bridge, an L2, or an AI agent, can verify with a SINGLE on-chain
`verifyProof` call, at a cost independent of the validator count N, that an Aere block reached
post-quantum finality, WITHOUT trusting the classical ECDSA seals and WITHOUT any change to the
base QBFT consensus.
The certificate is what a relying party checks when it wants a finality signal that survives a
quantum adversary. The classical path (the ECDSA committed-seal quorum, proven by
`AereZkQbftLightClient`) is trust-minimized but NOT quantum-safe: the seals are secp256k1 ECDSA,
and the Groth16 wrap is over BN254, both Shor-breakable. The certificate is the additive,
quantum-durable finality signal layered on top of the same live blocks.
---
## 2. Architecture: validators dual-sign, a zkVM aggregates, one on-chain verify
### 2.1 Validators dual-sign every finalized block
Each Aere validator holds two independent keys:
- an ECDSA (secp256k1) CONSENSUS key, used exactly as today to sign the QBFT committed seal that
finalizes a block. This is unchanged. Consensus is untouched.
- a HASH-BASED ATTESTATION key (Winternitz-XMSS / leanSig style), registered in
`AerePQAttestationKeyRegistry`, used ONLY to sign an additive post-quantum attestation over each
finalized block.
The attestation message is domain-bound so it cannot be replayed to another chain or purpose:
`msg = keccak256(abi.encode(attestationDomain, blockHash, blockHeight))`, where
`attestationDomain = keccak256(DOMAIN_PREFIX, chainId, registry)`. Because the ECDSA seal and the
hash-based attestation are separate signatures over separate keys, a quantum adversary that can
forge ECDSA still cannot forge the hash-based leg.
Hash-based signatures (XMSS, WOTS+, leanSig, SLH-DSA) are ONE-TIME or FEW-TIME: a given key can
sign only a bounded number of messages before its security degrades. The registry therefore treats
attestation keys as ROTATING: each validator advances a monotonic `keyEpoch` as it registers a
successor key, and the append-only history retains every key ever used.
### 2.2 The off-chain zkVM aggregates the quorum into one proof ([MEASURE])
An off-chain aggregator collects the validators' hash-based attestations for a finalized block and
runs an aggregation guest program inside a zkVM (the SP1 zkVM in the reference design) with:
- PRIVATE witness: the enrolled validators' attestation public keys, and the hash-based signatures
over the domain-bound block message.
- The circuit MUST, inside the zkVM:
1. recompute the `validatorSetRoot` from the witnessed keys and their epochs (and abort if it
does not match the committed root),
2. verify each counted attestation as a valid hash-based signature against a DISTINCT committed
key over `msg`,
3. never double-count a validator,
4. count the DISTINCT valid attestations and require the count to reach `ceil(2N/3)`,
5. output the public values below.
- PUBLIC output (the fixed 192-byte layout the on-chain verifier decodes):
`abi.encode(bytes32 blockHash, uint256 blockHeight, bytes32 validatorSetRoot,
uint256 validatorSetSize, uint256 quorumCount, bytes32 attestationDomain)`.
The proof is succinct: its size and its on-chain verification cost are fixed by the SP1 verifier,
independent of N and of how many attestations the circuit checked internally.
THIS CIRCUIT IS NOT IMPLEMENTED IN THIS REPOSITORY. It is real, novel cryptographic engineering
and is the [MEASURE] research component. See Section 6.
### 2.3 The on-chain half verifies one proof (BUILT and TESTED)
`AerePQAttestationKeyRegistry`:
- The owner (Aere Foundation, later the Timelock) enrolls the validator addresses that count toward
the quorum, mirroring how QBFT consensus enrollment is curated off-chain via the Foundation
Ledger and QBFT extraData. Enrollment fixes each validator's position in the root ordering.
- Each validator registers its OWN hash-based attestation key (the owner cannot forge a validator's
PQ key). Rotation is the same call again: it appends a new record, marks the predecessor
superseded, and advances the key-epoch. Append-only, fail-closed, no silent rewrite.
- `validatorSetRoot()` is a deterministic commitment to the current set and their current keys:
`leaf_i = keccak256(abi.encode(validator_i, keyEpoch_i, keyHash_i))`, and
`root = keccak256(abi.encode(leaf_0, ..., leaf_{N-1}))` in enrollment order. Any rotation bumps an
epoch and changes a key hash, so the root changes, and a certificate proven against the old root
stops matching automatically. A Merkle tree is a drop-in alternative if inclusion proofs are
wanted; a flat commitment suffices because the circuit witnesses the whole set.
`AereFinalityCertificateVerifier`:
- `verifyCertificate(publicValues, proof)` binds the proof's public values to the LIVE registry
(current root and size), enforces the quorum threshold, and makes a SINGLE `verifyProof` call to
the deployed SP1 gateway. It is fail-closed: it rejects unless ALL hold:
- the registry validator set is non-empty,
- `publicValues.validatorSetRoot == registry.validatorSetRoot()` (a rotation invalidates old
certificates),
- `publicValues.validatorSetSize == registry.validatorCount()`,
- `publicValues.attestationDomain == attestationDomain` (blocks cross-chain / cross-purpose
replay),
- `publicValues.quorumCount >= ceil(2N/3)`,
- `publicValues.blockHash != 0`,
- the SP1 proof verifies against the pinned circuit vkey (`verifyProof` REVERTS on an invalid
proof).
- `assertFinalizes(claimedBlockHash, claimedHeight, publicValues, proof)` additionally requires the
proof to finalize EXACTLY the claimed block, so a certificate for a different block cannot be
substituted. This is the call a bridge uses when releasing funds for a specific block.
- `recordCertificate(publicValues, proof)` verifies (strict) and records the certificate keyed by
block hash. `isFinalPQ(blockHash)` then returns true, which is the gate a consumer contract
checks. Recording is permissionless: any prover can submit a valid certificate.
The circuit is pinned. `PROGRAM_VKEY` is fixed at construction, so no caller can substitute a
different circuit. The deployed SP1 verifier gateway on Aere Network mainnet (chain 2800) is the
canonical Succinct gateway at `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`, the same gateway the
parallel-execution validity contracts and the zk light clients already call.
---
## 3. Why it is additive: no consensus flip, no re-genesis
The certificate reads the chain; it does not change how the chain is produced.
- No consensus change. Block production and finalization remain Besu QBFT over classical ECDSA
committed seals. The certificate is computed from an ADDITIONAL signature validators produce off
the hot path. If the aggregator is down, or no certificate is ever produced, consensus is
unaffected: blocks still finalize on ECDSA exactly as today. The certificate is "permissionless
but not automatic", the same property the zk light clients already have.
- No re-genesis. The attestation keys live in an ordinary contract, and the certificate is verified
by an ordinary contract. Adding this feature is a contract deployment plus validators registering
keys. It touches no genesis state and no client fork rule. This is a strictly weaker requirement
than the blocking hybrid consensus path (which the in-place activation work showed can itself now
be armed WITHOUT a re-genesis via a contract-anchored fork activation; the certificate needs even
less, because it never gates a block).
- Separation of concerns. The blocking hybrid ECDSA-plus-Falcon consensus path (formally proven
no-worse-than-ECDSA in `aerenew/formal-consensus/falcon_hybrid_dualquorum_smt.py`, with in-place
activation proven in the PQ-INPLACE-ACTIVATION work) makes CONSENSUS itself refuse a block
without a post-quantum quorum. The certificate is the READ side: it lets external parties verify
a post-quantum finality quorum without waiting for, or depending on, the consensus flip. The two
compose: a chain running the blocking hybrid can ALSO emit certificates, and a chain not yet
running it can emit certificates today as an attestation-only signal.
---
## 4. The quantum-safety argument
The certificate is Shor-resistant end to end, which is precisely what the classical finality path
is not.
- The attestation legs are HASH-BASED signatures. XMSS, WOTS+, leanSig, and SLH-DSA derive their
security from the collision and preimage resistance of a hash function ALONE. There is no lattice
and no discrete-log assumption. Grover's algorithm gives at most a square-root speedup against a
hash, which is answered by doubling the output size; there is no known quantum attack that breaks
a well-parameterized hash-based signature. This is the most conservative post-quantum assumption
available, weaker than the lattice assumptions behind Falcon or ML-DSA.
- The aggregation proof is a HASH-BASED STARK. Its soundness rests on hashing, not on pairings, so the
proof itself is Shor-resistant. (Scope note, 2026-07-19 finding: the underlying zkVM proof systems
are hash-based, but the pinned SP1 6.1.0 is a Hypercube / BaseFold multilinear system, not a
FRI-based STARK; the hash-based hence Shor-resistant reasoning is unchanged, only the "FRI" label is
corrected. The on-chain BabyBear + FRI verifier skeleton at 0x0AE8 verifies Aere's OWN Plonky3
aggregation STARK, not an SP1 6.1.0 proof.) This is the
crucial difference from a Groth16 SNARK over BN254: BN254 pairings are Shor-breakable
(Assumption A-9 in the engineering security spec is explicit that the BN254 verifiers stand only
until a hash-based STARK verifier replaces them). A certificate whose proof is a native STARK does
not carry a classical cryptographic leg.
Contrast with the classical path. `AereZkQbftLightClient` proves the ECDSA committed-seal quorum;
the seals are secp256k1 (Shor-breakable) and the on-chain proof is Groth16 over BN254 (Shor-
breakable). Under a cryptographically-relevant quantum adversary, that path offers no guarantee.
The certificate is designed so that neither the attestation nor the proof has that weakness.
HONESTY on this claim. The quantum-safety property is a property of the DESIGN and of the
[MEASURE] circuit ONCE IT IS REAL. The on-chain contract alone does not establish it: today the
contract is verified against a mock gateway, and if the certificate were wrapped to Groth16 (an
SP1 option) instead of proven as a native STARK, it would reintroduce a BN254 leg and lose the
end-to-end property. The Shor-resistant claim holds only for a native hash-based STARK proof over a
correctly implemented hash-based aggregation circuit. The direct hash-based STARK verifier on Aere
(the `AerePQStarkVerifier` line of work) is the on-chain counterpart that keeps even the proof
verification quantum-safe.
---
## 5. Consumer use cases
The certificate is a finality oracle a relying party gates on. In every case the consumer calls
`isFinalPQ(blockHash)` (or `assertFinalizes` for a specific block) and acts only on a recorded,
proof-backed post-quantum quorum.
- LIGHT CLIENT. A trust-minimized client of Aere finality that wants a quantum-durable signal
advances its view only on certificates, so its finality assumption reduces to "a quorum of Aere
validators held their hash-based keys", with no dependence on ECDSA or BN254 staying unbroken.
- BRIDGE. A bridge out of Aere releases funds for block X only after `assertFinalizes(X, ...)`
succeeds, so a future quantum adversary that forges the ECDSA seals still cannot forge a finality
certificate and cannot trick the bridge into releasing against a non-final block.
- L2 / ROLLUP. An L2 that settles to Aere, or reads Aere as a data or finality layer, gates its
own state transitions on the post-quantum certificate, inheriting Aere finality without inheriting
its classical cryptographic assumptions.
- AI AGENT. An autonomous agent that acts on Aere state (an x402 / AERE402 settlement agent, a
treasury bot) checks `isFinalPQ` before treating a payment or state change as settled, so its
actions rest on a quantum-safe finality signal rather than on a classical seal it cannot itself
audit.
The on-chain verification cost is independent of the validator count N: the verifier does no
per-validator work, it compares six words of public values against the registry and makes one
`verifyProof` call. A larger validator set does not raise the consumer's gas.
---
## 6. Honest status and the research frontier
BUILT and TESTED (this repository):
- `AerePQAttestationKeyRegistry.sol`: governed validator enrollment, self-custodied per-validator
hash-based attestation keys, append-only key history with monotonic key-epochs and rotation, a
deterministic `validatorSetRoot`, and fail-closed views.
- `AereFinalityCertificateVerifier.sol`: the public-input binding scheme, the `ceil(2N/3)` quorum
threshold, fail-closed strict / non-reverting / claimed-block verification, permissionless
certificate recording, and the `isFinalPQ` consumer gate. The pinned circuit vkey and the
domain binding are fixed at construction.
- 12 passing Hardhat tests against `MockSp1Verifier`, the same deployed-gateway test double used by
`AerePQAggregate` and the zk light clients. Its `verifyProof` REVERTS on an invalid proof, so the
fail-closed path is exercised exactly as against the production gateway. Tests cover: a 7-validator
set with hash-based keys registers and the quorum threshold is 5; a valid certificate with quorum
5 records and `isFinalPQ` returns true; a below-quorum certificate rejects; a wrong validator-set
root, wrong size, and wrong domain reject; a wrong block (and wrong height, and zero block hash)
rejects; an invalid proof rejects fail-closed; key rotation changes the set root and an
old-root certificate no longer matches while a fresh one records; and an empty validator set fails
closed.
NOT implemented, [MEASURE], research frontier:
- The OFF-CHAIN XMSS / leanSig aggregation zkVM guest circuit. Verifying N hash-based signatures
against the validator-set root inside a zkVM and emitting the public values is real, NOVEL
cryptographic engineering that is NOT in this repository. The on-chain verifier is proven against
a mock gateway that asserts the bound public values; a real end-to-end system additionally needs
the real circuit and a real SP1 proof.
- Which hash-based scheme is the tractable first target is itself a research question. Stateful XMSS
verification is hash-heavy but has no floating point, which is friendlier to a zkVM than Falcon's
FFT and Gaussian sampling. leanSig (the hash-based signature line behind Ethereum's Lean Consensus
direction) is designed for exactly this aggregation setting and is the natural target. An
ML-DSA-in-zkVM approach (integer arithmetic, FIPS 204) is a lattice alternative that trades the
most-conservative hash-only assumption for an easier circuit; it is a valid target but is not
hash-based, so it does not carry the hash-only quantum-safety argument of Section 4.
- The real end-to-end proving cost and latency, and the real-gateway on-chain verify gas, are
[MEASURE] and depend on the circuit implementation and the proof system (native STARK vs Groth16
wrap).
LIVE-FIRST framing. Aere Network can ship this ahead of Ethereum's Lean Consensus (targeted around
2027) precisely BECAUSE N is small. With a 7-validator set the aggregation circuit verifies at most
7 hash-based signatures per certificate, a tractable proving workload, whereas Ethereum's hundreds
of thousands of validators make the same aggregation a far larger circuit and a longer research
runway. The small validator set that makes Aere's honest-quorum premise an operator assumption is
the SAME property that makes post-quantum finality aggregation buildable now. This is stated as a
capability of the design, not a claim that the [MEASURE] circuit is finished.
Do NOT claim the certificate works end-to-end, and do NOT imply Aere consensus is post-quantum.
The claim this repository supports: the on-chain certificate verifier and the attestation-key
registry are built, fail-closed, and tested against a mock gateway; the design is additive and
needs no consensus flip and no re-genesis; and the quantum-safety argument holds for the design and
for the [MEASURE] circuit once it is real.
---
## 7. References
- On-chain SP1 gateway pattern: `contracts/contracts/mpc/AerePQAggregateVerifier.sol`,
`contracts/contracts/interop/AereZkQbftLightClient.sol` (the CLASSICAL ECDSA finality light
client this certificate is the post-quantum counterpart to).
- Registry pattern: `contracts/contracts/pqc/AerePQCKeyRegistry.sol`,
`contracts/contracts/validators/AereValidatorManifest.sol`.
- Consensus-side backing: `aerenew/formal-consensus/falcon_hybrid_dualquorum_smt.py`
(hybrid dual-quorum proven no-worse-than-ECDSA), the PQ-CONSENSUS-STEP2 and
PQ-INPLACE-ACTIVATION work (blocking hybrid consensus with in-place, no-re-genesis activation).
- Quantum scope boundary: `aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md` (signatures and
accounts can be post-quantum; consensus and the BN254 zk verifiers are classical; the chain as a
whole is not post-quantum).
- Direct hash-based STARK verifier line: `contracts/contracts/zkverify/AerePQStarkVerifier.sol`.

61
AERE-PQ-SCREEN-SUMMARY.md Normal file
View File

@ -0,0 +1,61 @@
# Spec 7: AerePQ-Screen (post-quantum compliance passport) - SUMMARY
## What was built
Institution-grade post-quantum compliance passport for Aere Network, built ON the existing compliance
stack (AereZKScreen, AereCompliancePool, AereSanctionsRegistry, AerePQCKeyRegistry) without modifying
any deployed contract. Three new contracts + 1 test + 1 doc.
### Files (all new)
- `aerenew/contracts/contracts/compliance/AereTrustRegistry.sol`
- `aerenew/contracts/contracts/compliance/AereVerifiableCredential.sol`
- `aerenew/contracts/contracts/compliance/AereBitstringStatusList.sol`
- `aerenew/contracts/test/aere-pq-screen.test.js`
- `aerenew/docs/AERE-PQ-SCREEN.md`
### 1. AereTrustRegistry (federated accredited issuers, eIDAS-2.0-in-spirit)
- Foundation (Ownable, OZ v4.9.6) = trusted-list operator. Federated SET of accredited issuers.
- Issuer entry: controller/address (issuer id), did:aere DID, scope (bytes32 credential-type tags),
status (None/Active/Suspended/Revoked), optional Falcon-512 pqcKeyId in live AerePQCKeyRegistry.
- Fail-closed isAccredited; append-only status + scope history (no silent rewrite); terminal revocation.
- verifyIssuerSignature() routes Falcon-512 verify to live AerePQCKeyRegistry.verifyWithKey -> 0x0AE1.
### 2. AereVerifiableCredential (W3C VC 2.0 anchor + verify)
- Credential struct: issuerId, subjectDidHash (did:aere, hash only, no PII), credentialType, validFrom/
validUntil, statusListId+statusIndex (W3C BitstringStatusListEntry), claimsHash (off-chain claims commit).
- verifyPresented / isValid check: accredited + Falcon-512 sig via 0x0AE1 + within-validity + not-revoked.
- anchorCredential fail-closed, relayable (authorship = PQC sig); isValid re-checks live (flips on
suspend/revoke). anchorCredentialWithDid binds+emits did:aere string (enforces prefix + hash match).
### 3. AereBitstringStatusList (W3C Bitstring Status List + zk non-revocation)
- On-chain bitset (256-bit words), Revocation (monotonic/terminal) or Suspension (reversible) purpose.
- Controller-only mutation, no silent no-op. getWord exposes raw words for off-chain commitment.
- zk non-revocation: INonRevocationVerifier (SP1 gateway ABI: verifyProof reverts on invalid),
public-input shape DEFINED (chainId, statusListContract, statusListId, statusRoot,
credentialCommitment, purpose), on-chain freshness binding (statusRoot==publishedRoot AND
rootEpoch==epoch), fail-closed NonRevocationNotConfigured. The SP1 CIRCUIT itself = [MEASURE].
## REAL test result
`npx hardhat test test/aere-pq-screen.test.js` from aerenew/contracts:
**12 passing (2s).** solc 0.8.23. Uses repo MockPQCPrecompile at 0x0AE1 (scheme 1 Falcon-512) via
hardhat_setCode, same as erc8004-adapters test. Covers: accreditation lifecycle + append-only history +
terminal revocation + fail-closed accreditation; PQC-signed credential verify+anchor (0x0AE1),
relayable; revocation via status list flips verify to false; wrong-scope issuer cannot issue; tampered
Falcon sig fails closed (nothing anchored); issuer-suspension fail-closed + reinstatement recovery;
expiry rejection; did:aere subject binding (mismatch + non-aere rejected); zk non-revocation stub shape
+ freshness (mock SP1) + fail-closed unconfigured path.
## [MEASURE] flags
- SP1 non-revocation circuit NOT implemented (interface + public-input shape + freshness binding are).
- Bitstring status root is controller-attested via publishStatusRoot; on-chain/in-VM derivation pending.
## [VERIFY] flags
- Exact W3C VC 2.0 JSON serialization / member names.
- ONCHAINID claim-topic <-> credentialType mapping for concrete ERC-3643 interop.
- Finalized did:aere method registration + full DID Core syntax validation.
- W3C-recommended minimum status-list size (herd privacy) at deployment.
## Scope honesty
- PQC signature path is REAL (Falcon-512 via live 0x0AE1); consensus stays classical ECDSA QBFT.
- No PII on chain (DIDs are identifiers; claims committed by hash; status list is anonymous bitset).
- Institutional ADOPTION / eIDAS accreditation is a partnership/regulatory matter, NOT claimed in code.
- No em-dashes in prose; brand "Aere Network"/"Aere", ticker AERE.

207
AERE-PQ-SCREEN.md Normal file
View File

@ -0,0 +1,207 @@
# AerePQ-Screen: an institution-grade post-quantum compliance passport
AerePQ-Screen upgrades Aere Network's compliance stack so it interoperates with the identity
standards banks and regulated institutions already use, W3C Verifiable Credentials 2.0, Decentralized
Identifiers (DID), and the ERC-3643 / ONCHAINID accreditation model, and adds the differentiator no
other stack carries today: issuer attestations that are POST-QUANTUM signed (Falcon-512) and verified
in full on-chain via Aere's live precompile at 0x0AE1.
It is built ON the existing compliance contracts (AereZKScreen, AereCompliancePool, AereSanctionsRegistry,
AereTravelRuleHashRegistry, and the live AerePQCKeyRegistry), not in place of them. It adds three new
contracts under `aerenew/contracts/contracts/compliance/` and does not modify any deployed contract.
## Honest status (read this first)
- CONTRACTS: built and tested. Three new Solidity 0.8.23 contracts, 12 passing Hardhat tests
(`test/aere-pq-screen.test.js`), verified against the repo's MockPQCPrecompile at 0x0AE1 exactly as
the erc8004 and PQC test suites do.
- POST-QUANTUM SIGNATURE PATH: real. Issuer attestations are Falcon-512 signatures verified on-chain
through the live AerePQCKeyRegistry, which routes to precompile 0x0AE1 (live on chain 2800,
activation block 9,189,161). The local test exercises this via the faithful mock; the real
precompile is separately proven against live mainnet via eth_call, as the rest of the PQC stack is.
- ZK NON-REVOCATION CIRCUIT: **[MEASURE]**. The on-chain W3C Bitstring Status List, the verifier
interface, the public-input shape, and the freshness binding are all built and tested. The SP1
circuit that actually produces a zero-knowledge non-revocation proof is NOT yet implemented. It is
to be built on Aere's existing SP1 zk stack (the same stack behind AereZKScreen).
- CONSENSUS SCOPE: unchanged. Aere consensus stays classical secp256k1 ECDSA QBFT. What is
post-quantum here is the AUTHENTICITY of compliance attestations, an application-layer property.
- ADOPTION AND ACCREDITATION: NOT claimed. Whether any real institution issues credentials here, and
whether accreditation in this registry carries legal weight under eIDAS 2.0 or any regime, is a
partnership and regulatory matter external to this code. Deploying these contracts asserts none of
it. This document describes what the code does, not who uses it.
## Architecture
The flow is Trust Registry, then VC issuance, then Bitstring status, then zk non-revocation.
```
Aere Foundation (trusted-list operator, registry owner)
|
| accredits issuers (scope + Falcon key ref)
v
AereTrustRegistry ----- verifyIssuerSignature() -----> AerePQCKeyRegistry (LIVE)
federated set of verifyWithKey() --> 0x0AE1 Falcon-512
accredited issuers (in-full on-chain PQC verify)
|
| isAccredited(issuerId, type)
v
AereVerifiableCredential ---- statusOf(list, index) ----> AereBitstringStatusList
W3C VC 2.0 anchor + verify: W3C Bitstring Status List
1. issuer accredited? revocation / suspension bitset
2. Falcon-512 issuer signature valid? (via 0x0AE1) + zk non-revocation verifier
3. within validity window? interface + public-input shape
4. not revoked? (status list bit == 0) (SP1 circuit = [MEASURE])
```
### 1. AereTrustRegistry (federated accredited issuers)
Trust is not a single Foundation key. It is a federated SET of accredited issuers, each with a DID,
an accreditation scope (which credential types it may issue), a lifecycle status, and an optional
Falcon-512 key reference. This mirrors, in spirit, the eIDAS 2.0 / EUDI trusted-list model, in which
a supervisory body maintains a machine-readable list of trust service providers and the services each
is accredited for. Here the Foundation is the trusted-list operator (the registry owner).
- Each issuer entry: `controller` (the issuer id / address), `did` (a did:aere DID, a public
identifier and not PII), accreditation `scope` (a set of opaque bytes32 credential-type tags),
`status` (Active / Suspended / Revoked), and `pqcKeyId` (an optional Falcon-512 keyId in the live
AerePQCKeyRegistry, sentinel `NO_KEY` when absent).
- FAIL-CLOSED: `isAccredited(issuerId, type)` is true only when the issuer is Active and the type is
in scope. Unknown, suspended, and revoked issuers, and out-of-scope types, all resolve to false.
- APPEND-ONLY HISTORY, NO SILENT REWRITE: every status transition and scope change is pushed to an
append-only log. The current status is a pointer; how it got there is immutable and auditable.
- TERMINAL REVOCATION: accreditation revocation cannot be reversed; suspension can.
- `verifyIssuerSignature(issuerId, message, sig)` verifies a Falcon-512 issuer attestation over a
32-byte message via the live AerePQCKeyRegistry (which calls precompile 0x0AE1), fail-closed.
### 2. AereVerifiableCredential (W3C VC 2.0 anchor + verify)
A credential binds an accredited `issuerId`, a subject `did:aere` DID (bound by its hash; the DID
string is carried off-chain, no PII on chain), a `credentialType`, a validity window (`validFrom` /
`validUntil`, the W3C VC 2.0 member names), a W3C BitstringStatusListEntry (`statusListId` +
`statusIndex`), and a `claimsHash` (keccak256 of the off-chain credentialSubject claims, so the anchor
commits to content without publishing it).
Verification (`verifyPresented`, and the live `isValid` for anchored credentials) checks, fail-closed:
1. the issuer is ACCREDITED for the credential type (AereTrustRegistry);
2. the issuer's Falcon-512 signature over the credential digest verifies on-chain via 0x0AE1;
3. the credential is WITHIN its validity window (not-yet-valid and expired both fail);
4. the credential is NOT REVOKED per its Bitstring Status List entry.
`anchorCredential` runs the full path fail-closed and records the credential once; authorship is the
post-quantum signature alone, so anchoring is relayable by any sender. `isValid` then re-checks a
recorded anchor LIVE, so it flips to false the instant the issuer is suspended or revoked or the
credential's status bit is set. `anchorCredentialWithDid` additionally binds and emits the subject's
`did:aere` DID string (enforcing the hash match and the `did:aere:` prefix).
The DID method is `did:aere`, consistent with AereIdentityRegistry8004's `did:aere:<chainId>:<id>`.
### 3. AereBitstringStatusList (W3C Bitstring Status List + zk non-revocation)
An on-chain implementation of the W3C Bitstring Status List v1.0 mechanism: a compact bitset where
each credential is assigned a status index, and the bit at that index encodes its status (default
0 = active). Revoking a credential flips one bit. Because one list packs many credentials, a verifier
learns only the queried bit, giving herd privacy.
- A list is created for one W3C statusPurpose: Revocation (bits are monotonic and terminal) or
Suspension (bits are reversible). Bits are packed 256 to a word and exposed via `getWord` so anyone
can recompute a commitment over the full bitstring off-chain.
- Controller-only mutation; a re-set of the same value reverts (no silent no-op).
**Zero-knowledge non-revocation ([MEASURE] circuit).** The plain read `statusOf(list, index)` reveals
the queried index. For privacy-preserving verification, a holder can instead prove in zero knowledge
that "the status bit of the credential I hold is 0" (my credential is not revoked) WITHOUT revealing
which index, bound to the list's current committed state. This file defines the verifier interface
and the exact public-input shape; the SP1 circuit is not yet built.
Public-input shape (the SP1 program's committed public values), ABI-encoded exactly as:
```
abi.encode(
uint256 chainId, // must equal block.chainid
address statusListContract, // must equal the AereBitstringStatusList address
uint256 statusListId, // the list the credential's status lives in
bytes32 statusRoot, // commitment to the bitstring the proof was made against
bytes32 credentialCommitment, // hiding commitment to the holder's credential (binds a real
// credential without revealing its status index)
uint8 purpose // must equal the list's statusPurpose
)
```
In-circuit private witness (**[MEASURE]** to be implemented): the status index `i`, an opening of
`statusRoot` at `i` proving `bitstring[i] == 0`, and an opening of `credentialCommitment` to the
credential, so the statement proven is "there is an index i, committed by credentialCommitment, whose
bit under statusRoot is 0", revealing neither `i` nor the credential.
On-chain, `verifyNonRevocation` enforces chainId / contract / listId / purpose, requires `statusRoot`
to equal the list's published root AT THE CURRENT EPOCH (so a stale proof is rejected), then delegates
the bit-is-0 statement to the SP1 verifier (reverts on an invalid proof, the canonical SP1 gateway
ABI). It is fail-closed: it reverts `NonRevocationNotConfigured` when no verifier or vkey is set, so
"unconfigured" can never be mistaken for "not revoked". **[MEASURE]** In this version the status root
is controller-attested via `publishStatusRoot`; a future version derives the root on-chain or inside
the SP1 VM from the raw words so it is trustless. The raw words are exposed regardless.
## Standards alignment
- **W3C Verifiable Credentials 2.0.** The Credential struct maps to the VC 2.0 data model: `issuer`
(the accredited issuer), `credentialSubject` (the subject DID + off-chain claims committed by
`claimsHash`), `validFrom` / `validUntil`, `type` (the `credentialType` tag), and `credentialStatus`
(a BitstringStatusListEntry: `statusListId` + `statusIndex`). The contract anchors a COMMITMENT to
the VC (digest + claims hash), not the JSON-LD document. [VERIFY] the exact JSON member names and
serialization against the finalized VC 2.0 spec before claiming wire-level conformance.
- **W3C Bitstring Status List v1.0.** Implemented directly on-chain (bitset + statusPurpose +
status index). [VERIFY] the W3C-recommended large minimum list size for herd privacy is a
deployment policy choice and is not enforced by the contract.
- **DID / did:aere.** Subjects and issuers use `did:aere`, consistent with AereIdentityRegistry8004.
The contract enforces the `did:aere:` prefix on the binding path; full DID Core syntax validation is
off-chain. [VERIFY] against a finalized did:aere method registration.
- **ERC-3643 / ONCHAINID.** ERC-3643 (T-REX) gates permissioned tokens on ONCHAINID identities that
carry signed CLAIMS from trusted claim issuers held in a Trusted Issuers Registry. AerePQ-Screen
provides the same three primitives, in the same shape: AereTrustRegistry is the trusted-issuers
registry (with scoped accreditation), AereVerifiableCredential is the claim (issuer-signed, typed,
time-bounded, subject-bound), and AereBitstringStatusList is claim revocation. The ERC-3643 claim
signature is ECDSA; AerePQ-Screen's is additionally Falcon-512. [VERIFY] a concrete ONCHAINID
claim-topic mapping (claim topic <-> `credentialType`) before asserting drop-in ERC-3643 interop.
- **eIDAS 2.0 / EUDI trusted lists.** The federated accredited-issuer model mirrors the EUDI trusted
list, where a supervisory body maintains a machine-readable list of trust service providers and
their accredited services. This is a STRUCTURAL alignment. Legal qualification under eIDAS is
external and is NOT claimed.
## The post-quantum differentiator
Every institutional identity stack in production, ERC-3643 / ONCHAINID claims, EUDI wallet
attestations, classic PKI credentials, authenticates the issuer's attestation with ECDSA or RSA,
which a cryptographically relevant quantum computer forges (harvest-now, decrypt-later applies to
long-lived compliance attestations especially). AerePQ-Screen binds an optional Falcon-512 key to each
issuer and verifies the issuer's attestation with a NIST post-quantum signature scheme in full
on-chain via the live precompile 0x0AE1. That is the property banks and regulated institutions cannot
get elsewhere, and it is the reason this passport exists.
## Files
- `aerenew/contracts/contracts/compliance/AereTrustRegistry.sol`
- `aerenew/contracts/contracts/compliance/AereVerifiableCredential.sol`
- `aerenew/contracts/contracts/compliance/AereBitstringStatusList.sol`
- `aerenew/contracts/test/aere-pq-screen.test.js` (12 passing tests)
## Test result
`npx hardhat test test/aere-pq-screen.test.js` from `aerenew/contracts`: 12 passing. Covers
accreditation lifecycle (append-only history, terminal revocation, fail-closed accreditation),
credential issue and verify with a Falcon-512 signature via the 0x0AE1 mock, revocation via the
Bitstring Status List flipping verification to false, a non-accredited (wrong-scope) issuer being
unable to issue, a tampered PQC signature failing closed, fail-closed behavior on issuer suspension
with recovery on reinstatement, expiry rejection, did:aere subject binding, and the zk non-revocation
stub's public-input shape with freshness binding (mock SP1 verifier) plus its fail-closed
unconfigured path.
## Open items ([MEASURE] / [VERIFY])
- **[MEASURE]** Implement the SP1 non-revocation circuit for the public-input shape defined above,
publish its vkey via `setNonRevocationVKey`, and wire the real SP1VerifierGateway.
- **[MEASURE]** Derive the Bitstring Status List root on-chain or in the SP1 VM (currently
controller-attested via `publishStatusRoot`).
- **[VERIFY]** Exact W3C VC 2.0 JSON serialization and member names.
- **[VERIFY]** Concrete ONCHAINID claim-topic to `credentialType` mapping for ERC-3643 interop.
- **[VERIFY]** Finalized did:aere method registration and full DID syntax validation policy.
- **[VERIFY]** W3C-recommended minimum status-list size (herd-privacy policy) at deployment.

View File

@ -0,0 +1,682 @@
# Aere Network Protocol Specification
**Document:** spec-24-protocol-spec
**Chain:** Aere Network mainnet, chain ID 2800 (`0xaf0`)
**Base execution client:** Hyperledger Besu 26.4.0 line (fork), with a second client (patched Nethermind 1.39.0) as a validation cross-check
**Status:** Internal draft for adversarial review and for the external audit that follows. Every value in this document is intended to be checkable against the repository, the live chain, or a cited upstream specification. It is internally verified and adversarially self-reviewed; it is not yet externally audited. Read every parameter as "internally verified, not externally attested" until an independent audit completes.
---
## 0. How to read this document
This is a *delta* specification. It does not restate Ethereum execution semantics or the QBFT consensus protocol. It specifies, precisely and exhaustively, only what Aere Network *adds to* and *changes from* the union of two upstream specifications:
1. **Ethereum execution semantics**, as defined by the Ethereum execution-layer specifications ("execution-specs") and the Ethereum Yellow Paper, at the Pectra plus Fusaka ruleset.
2. **QBFT consensus**, as defined by the Hyperledger Besu QBFT implementation and the IBFT 2.0 / QBFT literature.
A second implementation team should be able to take a stock Besu (or a stock Nethermind), apply the changes in this document, and produce a client that matches Aere Network byte-for-byte on chain 2800. If a behavior is not described here as a delta, it is by definition identical to the referenced upstream, and the upstream specification governs.
**Honesty conventions used throughout.**
- A value that is directly sourceable from the repository, an AIP, or the live chain is stated plainly.
- A value that could not be confirmed from a primary source in the repository at authoring time carries an explicit **[VERIFY: ...]** note for a human to close before external publication. Section 8 collects every such flag.
- The **post-quantum scope boundary is absolute and repeated because it is the single most common thing to get wrong about Aere**: the post-quantum precompiles are an application-layer and account-layer verification surface. Consensus on Aere is classical secp256k1 ECDSA under QBFT. The on-chain zero-knowledge verifiers are classical BN254 pairing checks. **Aere consensus is not post-quantum, and this document never claims that it is.**
**Typographic note.** This specification avoids em-dashes in prose. Ranges and address bands use `..` (for example `0x0AE1..0x0AE5`). Field layouts in fenced blocks and tables use their own syntax.
---
## 1. Scope and non-scope
### 1.1 The delta framing
Aere Network did not invent a new protocol. Aere Network is:
```
Aere Network = Ethereum execution (Pectra + Fusaka ruleset)
+ QBFT consensus (Besu / IBFT 2.0 family)
+ the Aere delta specified in this document
```
The Aere delta is small and enumerable. It consists of exactly the following:
1. **Chain and consensus parameters** distinct from Ethereum mainnet: chain ID, block period (with one mid-chain transition), the QBFT validator-set size and quorum, and the genesis allocation. (Section 2, Section 3.)
2. **The AerePQC hard fork**: five native post-quantum verification precompiles at `0x0AE1..0x0AE5`, the native EIP-2935 write path, and the EIP-7939 CLZ opcode, all activated together at one block height. (Section 4.)
3. **Two further precompiles built but not activated on mainnet** (`0x0AE6` ML-KEM-768, `0x0AE7` Falcon HashToPoint) and one reference skeleton (`0x0AE8` hash-based STARK verify; the BabyBear/FRI skeleton targets Aere's own Plonky3 circuits, NOT SP1 6.1.0, which is Hypercube, see 4.8.3). Specified for completeness and clearly marked non-live. (Section 4.6.)
4. **Fee and burn mechanics**: a 1-Gwei EIP-1559 base-fee floor applied at the execution layer, and a validator-reward-cut burn applied *above* consensus by an application contract. (Section 5.)
5. **A set of deliberate EIP deviations** from Ethereum parity that follow from Aere being a QBFT chain with no beacon layer and no blob data-availability market. (Section 6.)
Everything else, meaning the EVM instruction set, gas schedule (except the additions in Section 4 and the floor in Section 5), transaction types, state trie, RLP encoding, receipt and log format, and JSON-RPC surface, is identical to the referenced Ethereum ruleset and is out of scope for this document.
### 1.2 Upstream references this document builds on (and does not restate)
| Ref | Title | Role in this spec |
|---|---|---|
| ES | Ethereum execution-layer specifications ("execution-specs") | The executable definition of EVM execution, state transition, and the gas schedule that Aere inherits unchanged. |
| YP | Ethereum Yellow Paper | The formal state-transition reference for the base layer. |
| QBFT | Hyperledger Besu QBFT consensus, and the IBFT 2.0 formal model (Saltini and Hyland-Wood, "IBFT 2.0: A Safe and Live Variation of the IBFT Blockchain Consensus Protocol for Eventually Synchronous Networks") | The consensus protocol Aere runs. Section 3 specifies only the Aere-specific parameters and does not redefine the message flow, which QBFT governs. |
| EIP-1559 | Fee market change for ETH 1.0 | Base-fee mechanics that Aere inherits, then floors (Section 5.1). |
| EIP-2935 | Serve historical block hashes from state | Aere activates the write path at the AerePQC fork (Section 4.5). |
| EIP-7825 | Transaction gas limit cap (2^24) | The per-transaction cap Aere keeps identical to Ethereum; it is the reason the PQC precompiles exist (Section 4). |
| EIP-7939 | Count leading zeros (CLZ) opcode | Activated at the AerePQC milestone (Section 4.7). |
| EIP-7702 | Set EOA account code | The EOA-delegation primitive Aere carries (Section 6). |
| FIPS 202 | SHA-3 / SHAKE | Basis of the `0x0AE5` SHAKE256 precompile. |
| FIPS 203 | ML-KEM | Basis of the non-live `0x0AE6` precompile. |
| FIPS 204 | ML-DSA | Basis of the `0x0AE3` precompile. |
| FIPS 205 | SLH-DSA | Basis of the `0x0AE4` precompile. |
| Falcon | NIST PQC round-3 Falcon | Basis of the `0x0AE1` and `0x0AE2` precompiles. |
| RIP-7212 / RIP-7951 | secp256r1 (P-256) verification precompile | The `0x100` precompile Aere shares with Ethereum's Osaka; standards-aligned, not Aere-proprietary. |
Anywhere this document is silent, the referenced upstream is authoritative.
### 1.3 Aere-specific source artifacts
The primary Aere sources this specification draws from, all in the repository:
- `aerenew/aips/AIP-3.md` (block period), `AIP-4.md` (PQC verifier suite and account layer), `AIP-7.md` (the AerePQC hard-fork activation).
- `aerenew/research/aip-draft-pqc-precompiles.md` (the precompile mechanism specification, including the as-shipped reconciliation of malformed-input behavior).
- `aerenew/contracts/contracts/pqc/AerePQCAttestation.sol` (the on-chain consuming contract that builds the exact precompile input; the authoritative byte-layout oracle for the live `0x0AE1..0x0AE4` precompiles, whose native source lives in the gold binary rather than the repository).
- `aerenew/pqc-fork/precompiles/*.java` (the source of the non-live `0x0AE6`, `0x0AE7`, and the `0x0AE8` skeleton).
- `aerenew/contracts/contracts/AereCoinbaseSplitterV2.sol` and `aerenew/contracts/contracts/sink/AereSink.sol` (burn and routing).
- `aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md`, `AERE-ENGINEERING-SECURITY-SPEC.md`, `AERE-ARCHITECTURE-MAP.md`.
- `aere-genesis-current.json` (the genesis config).
The build artifact of record for the fork is the Aere Besu fork, base `hyperledger/besu:26.4.0` (base commit `d203201`), gold binary SHA-256 `7c5c0743088e9fa765d54c745814601b3c7a27a892e396156f88092b34c80772`.
---
## 2. Chain parameters
This section states every chain-level parameter where Aere differs from an Ethereum mainnet default, plus the small set of core constants a reimplementer must pin exactly.
### 2.1 Identity and genesis
| Parameter | Value | Source / note |
|---|---|---|
| Chain ID | `2800` (`0xaf0`) | `aere-genesis-current.json` `config.chainId`. Used in EIP-155 replay protection and the EIP-2124 fork id. |
| Network genesis | genesis-v2, re-genesised 2026-05-07 | The current chain is the second genesis (see `AIP-3`, `project_chain_live`); block 0 of the live chain is genesis-v2. |
| Genesis timestamp | `0x69fbd960` | `aere-genesis-current.json` `timestamp`. |
| Native coin | AERE (ticker uppercase `AERE`) | The single native coin; there is no protocol mint path. |
| Total supply | 2,800,000,000 AERE, fixed at genesis | Sum of the six genesis allocations below. |
| Genesis difficulty | `0x1` | QBFT (proof-of-authority family); difficulty is nominal. |
| Genesis coinbase | `0x0000000000000000000000000000000000000000` | Genesis has no block reward beneficiary. |
Genesis allocation (fixed, 2.8B AERE total):
| Beneficiary | Amount (AERE) | Share | Address |
|---|---|---|---|
| Strategic Investor | 100,000,000 | 3.57% | `0xaee2f3989f0AB23296Fa3b92247fe67587141311` |
| Foundation (ops + signing) | 180,000,000 | 6.43% | `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` |
| Mining Reserve (emissions) | 1,400,000,000 | 50.00% | `0x038f59A40ceeCd599A4588E4B0ff4642a0fbfFB8` |
| Ecosystem Reserve | 560,000,000 | 20.00% | `0xB6a364F47d21DC2CbEB803565c111c1026e11C75` |
| Team Reserve | 420,000,000 | 15.00% | `0x7968C438204a78B4e032fcFFd9A56Edb15fdCCdf` |
| Airdrop Reserve | 140,000,000 | 5.00% | `0x261913fA73D6F109382F1aE98Ff6822ff03628B1` |
There is no protocol-level inflation or mint path in the state-transition rules; emissions come from moving pre-allocated Mining Reserve balances, not from minting.
### 2.2 Block period
Aere runs a shorter block period than Ethereum, and it changed once mid-chain via a QBFT configuration transition (not a re-genesis and not a client fork).
| Epoch | Block range | Target period | Mechanism |
|---|---|---|---|
| Genesis era | block 0 .. 2,138,450 | 1,000 ms | genesis `config.qbft.blockperiodseconds = 1` |
| Sub-second era | block 2,138,451 onward | 500 ms | QBFT `transitions.qbft` entry `{ "block": 2138451, "xblockperiodmilliseconds": 500 }` |
The transition key is `xblockperiodmilliseconds` (milliseconds), distinct from the seconds-based `blockperiodseconds` used for the initial value. State, balances, code, and the validator set were unchanged across the transition. Block intervals before 2,138,451 are approximately 1000 ms and after are approximately 500 ms, verifiable by `eth_getBlockByNumber` timestamp deltas. (Source: `AIP-3`.)
Finality is deterministic and single-slot: a committed block is final when produced (Section 3). "Sub-second finality" therefore means approximately 0.5 s worst-case inclusion-to-final latency in the sub-second era, not a probabilistic confirmation depth.
### 2.3 Gas limits, and the genesis gas-limit ambiguity stated honestly
| Parameter | Value | Note |
|---|---|---|
| Genesis block gas limit | `0x1fffffffffffff` = 9,007,199,254,740,991 (2^53 1) | `aere-genesis-current.json` `gasLimit`. This is effectively unbounded. |
| Per-transaction gas cap | `16,777,216` (2^24), EIP-7825 | Inherited identically from Ethereum's Fusaka ruleset; enforced on chain 2800. |
**The ambiguity, stated plainly.** The genesis declares a block gas limit of 2^53 1, which is not a meaningful economic block target; it is an effectively unbounded ceiling. Besu adjusts the block gas limit from the parent by at most 1/1024 per block (standard Ethereum block-gas-limit dynamics), so the *operative* block gas limit on the live chain is whatever the validators have converged the header field to since genesis, not the genesis 2^53 1 literally. The *binding* per-transaction constraint that matters for protocol design is the EIP-7825 cap of 2^24, which is identical to Ethereum L1 and is the reason the PQC precompiles (Section 4) exist at all. A reimplementer MUST set the genesis `gasLimit` field to `0x1fffffffffffff` to match block-0 hashing, and MUST enforce EIP-7825 per-transaction. The effective per-block gas limit on the live chain head is an operational value, not a genesis constant.
[VERIFY: the operative block gas limit on the live chain 2800 head (the header `gasLimit` field at recent heights), since genesis declares 2^53 1 but Besu block-gas-limit dynamics may have moved the operative value; confirm against `eth_getBlockByNumber("latest")`.]
### 2.4 QBFT consensus parameters
| Parameter | Value | Source |
|---|---|---|
| Consensus family | QBFT (Istanbul BFT / IBFT 2.0 family) | `AERE-ENGINEERING-SECURITY-SPEC` §1.1 |
| Live validator set size | N = 7 | Engineering spec §1.1; live set expanded from a smaller genesis set (Section 3.2). |
| Byzantine fault bound | f = 2 (at N = 7) | `f = floor((N-1)/3) = 2` |
| Commit quorum | 5 of 7 (`ceil(2N/3) = 5`, equivalently `2f + 1`) | Engineering spec §1.1 |
| Validator identity | secp256k1 ECDSA, recovered from the seal (`ecrecover`); no stored validator public key at verify time | Engineering spec §1.1 |
| Epoch length | 30,000 blocks | genesis `config.qbft.epochlength` |
| Request timeout | 4 seconds (base round timeout) | genesis `config.qbft.requesttimeoutseconds` |
| Finality | deterministic single-slot (a committed block is final) | Engineering spec §4.1 |
Genesis QBFT config (from `aere-genesis-current.json`):
```json
"qbft": {
"blockperiodseconds": 1,
"epochlength": 30000,
"requesttimeoutseconds": 4
}
```
The live chain additionally carries the block-2,138,451 `xblockperiodmilliseconds: 500` transition (Section 2.2). The `aere-genesis-current.json` file in the repository is a base config that does not itself carry the block-period transition or the Cancun/Prague/Osaka/futureEips fork-time fields; those are part of the running client's chain configuration and are enumerated in Section 2.5. A reimplementer must merge the transition and fork times into the config as specified.
[VERIFY: whether the authoritative running genesis/config on chain 2800 carries the Cancun/Prague/Osaka/futureEips time fields inline (they are absent from `aere-genesis-current.json` in the repo but are required to reproduce the fork schedule in Section 2.5).]
### 2.5 Fork schedule
Aere tracks Ethereum's hard forks so that its EVM surface matches mainnet at the same addresses and opcodes, then adds one Aere-specific milestone.
| Ethereum fork | Aere activation | Gate | Note |
|---|---|---|---|
| London, Shanghai | Genesis (block 0) | `londonBlock: 0`, `shanghaiTime: 0` | Active from genesis. |
| Cancun + Prague (Pectra) | Block 2,075,363 | `cancunTime == pragueTime == 1780189051` | Cancun and Prague share one activation instant on Aere. |
| Osaka (Fusaka) | Block 2,106,606 | `osakaTime == 1780220351` | RIP-7951 P-256 at `0x100` active from here. |
| AerePQC milestone | Block 9,189,161 | Besu `futureEipsTime == 1783820272` (2026-07-12 01:37:52 UTC) | PQC precompiles `0x0AE1..0x0AE5` + EIP-2935 write path + EIP-7939. |
| Base-fee floor | Block 10,141,734 | client flag `aere.basefee.floor.forkBlock` | 1-Gwei EIP-1559 base-fee floor (Section 5.1). |
The Cancun/Prague/Osaka activations are gated by timestamp in the config, so the block heights 2,075,363 and 2,106,606 are the observed first-block-past-timestamp and should be confirmed against the live head history.
[VERIFY: block heights 2,075,363 (Pectra) and 2,106,606 (Osaka) against the live chain head history; the config gates these by timestamp, not block number.]
---
## 3. Consensus (QBFT) delta and seal format
Aere runs QBFT unmodified in protocol; the delta is entirely in parameters (Section 2.4) and in operational posture. This section specifies what a reimplementer needs to reproduce Aere's block sealing and validation, and references QBFT for the message flow it does not restate.
### 3.1 What is inherited from QBFT (not restated here)
The following are governed entirely by the QBFT reference and are identical on Aere:
- The three-phase message flow (Pre-prepare / Prepare / Commit) within a round.
- Round-change (view-change) on timeout, with a doubling round-timeout backoff from the base `requesttimeoutseconds`.
- IBFT 2.0 locking: a validator that has prepared a block retains the prepared block and its prepare-certificate, carries the certificate in a round-change message, and a new proposer re-proposes the justified (locked) block rather than a conflicting one. This is the property that prevents a stale or conflicting proposal from being committed across a round change.
- Round-robin proposer selection over the ordered validator set.
- The RLP layout of the QBFT `extraData` header field (vanity, validator list, vote, round, committed seals) and the two hashing passes (the proposer-seal hash over the header with an empty committed-seal list, and the block hash over the header with the committed seals populated).
A reimplementer should take these directly from Besu QBFT. Aere changes none of them.
### 3.2 Validator set
The validator set is QBFT-managed on-chain via the standard QBFT validator-vote mechanism in the header `extraData`, not by any Aere-specific rule and not by any Ethereum beacon-layer deposit (Section 6 explains why the Pectra deposit/withdrawal machinery has no effect on the Aere validator set).
- **Genesis validator set.** The genesis `extraData` encodes the initial QBFT validator list. In `aere-genesis-current.json` the genesis `extraData` is:
`0xf865a00000...0000f83f94b5e768cb3117ea25175f51aa956b48691025c2a894f73bbbd26a82dbc769066550d8d4585eab2fc00d944bf6f91d84b37454544279cba152581ed6820044c080c0`
which decodes to a three-validator genesis set:
- `0xb5e768cb3117ea25175f51aa956b48691025c2a8`
- `0xf73bbbd26a82dbc769066550d8d4585eab2fc00d`
- `0x4bf6f91d84b37454544279cba152581ed6820044`
- **Live validator set.** The live set is N = 7 (f = 2, quorum 5), all Foundation-operated, expanded from the genesis set by QBFT votes at chain head (see `validator_expansion`). Enumerating the exact seven live validator addresses is an operational detail outside this protocol spec.
[VERIFY: the exact list of the seven live validator addresses at the current head, if they are to be published; the genesis `extraData` above encodes only the three-validator genesis set.]
### 3.3 Seal format (the part a reimplementer must match exactly)
Aere's seals are standard QBFT, restated here because a reimplementer must reproduce them byte-for-byte:
- **Committed seal.** Each committed seal is a **65-byte recoverable ECDSA signature** over secp256k1: `r` (32 bytes) `|| s` (32 bytes) `|| v` (1 byte recovery id). The signer (committer) address is recovered with `ecrecover` over the commit-seal pre-image (the block hash computed with an empty committed-seal list, per QBFT). There is no stored validator public key consulted at verification time; identity is the recovered address.
- **Proposer seal.** The proposer's seal in `extraData` is likewise a 65-byte recoverable ECDSA signature over the proposer-seal hash.
- **Quorum rule.** A block is valid only if it carries at least **5 distinct committed seals**, each recovering to a **distinct member of the current validator set**. Fewer than 5 distinct in-set committers, or any duplicate committer, invalidates the block.
- **Hash algorithm.** keccak-256, as in Ethereum. Block hashing, Merkle-Patricia state proofs, and commit-seal pre-images assume keccak-256 collision resistance (Assumption A-4 in the engineering spec).
The seal signature scheme is classical secp256k1 ECDSA. Forging a block reduces to forging secp256k1 ECDSA, which is classically hard and Shor-breakable under a cryptographically relevant quantum computer. **This is the precise reason Aere consensus is described as classical, not post-quantum.**
### 3.4 Safety and liveness envelope (specification of guarantees, not a proof)
At N = 7, f = 2, quorum 5:
- **Safety (no fork / no equivocation)** holds with up to 2 Byzantine validators. A 3-of-7 collusion (more than one third) can violate agreement; this bound is exact, not a soft target.
- **Liveness** is eventual under partial synchrony (Assumption A-1). At the exact f = 2 boundary (2 validators down, exactly 5 survivors with zero quorum slack) a rare, temporary, self-healing zero-progress window can occur; this is inherent to partially synchronous BFT (FLP), is not a fork, and is not a permanent halt. Adversarial soak testing (100 kill-2-of-7 cycles) recorded 0 forks and 100/100 resync.
- **Sub-quorum behavior.** With 3 or more validators down (4 or fewer alive, below the quorum of 5), the chain halts cleanly and resumes from the exact halted height when quorum is restored, with no re-genesis and no split.
These follow from the QBFT quorum math and the locking property, plus the fault-tolerance characterization; they are consensus guarantees, not Aere-specific protocol rules, and are reproduced here only to fix the parameter envelope. The load-bearing operational caveat is that all seven validators are Foundation-operated (Nakamoto coefficient about 1), which is a trust assumption, not a cryptographic guarantee.
### 3.5 Block-period transition as a consensus config rule
The only Aere-specific consensus *rule* beyond parameters is the timed block-period transition (Section 2.2), which a reimplementer must apply: from block 2,138,451 the QBFT block period is 500 ms rather than the genesis 1000 ms. This is a Besu QBFT `transitions` entry, applied automatically by any node synced from genesis against the correct config. It changes timing only and does not alter QBFT's fault tolerance.
### 3.6 A note on base-fee validation under QBFT
A consensus-relevant subtlety a reimplementer must know: the QBFT header ruleset on chain 2800 does not include a base-fee gas-price validation rule (the `BaseFeeMarketBlockHeaderGasPriceValidationRule` present in Besu's Clique and Merge rulesets). Consequently a change to the base fee, including the 1-Gwei floor of Section 5.1, cannot halt or split the chain: non-validating nodes follow the producer's header base fee automatically. Validators must be upgraded to the floor logic only for base-fee *consistency* across proposers (so the value does not oscillate by proposer), not to avoid a halt. (Source: base-fee floor runbook; memory `rpc_basefee_honesty_fix`.)
---
## 4. The AerePQC fork (the main delta)
The AerePQC hard fork is the single largest element of the Aere delta. It activated on mainnet chain 2800 at **block 9,189,161** (2026-07-12), configured as a Besu `futureEips` milestone with activation timestamp `futureEipsTime = 1783820272`. It is a coordinated, client-only, flag-day activation with **no re-genesis and no state migration**. It added three things, all at the execution layer:
1. Five native post-quantum verification precompiles at the address band `0x0AE1..0x0AE5`.
2. The native EIP-2935 historical-block-hash write path.
3. The EIP-7939 CLZ opcode.
It did **not** change how blocks are proposed, signed, or committed. Blocks are still produced and committed by the QBFT validator set signing classical secp256k1 ECDSA committed seals (Section 3). (Source: `AIP-7`.)
### 4.1 Common precompile conventions (as shipped)
These conventions apply to all five live verify/hash precompiles and are the *as-shipped* behavior, which differs from an earlier design draft. Where the draft `aip-draft-pqc-precompiles.md` said "MUST revert on malformed input" and "single-byte output", that was the original design intent; the reconciled, live behavior is below and is what a reimplementer MUST match.
- **Fail-closed, never fault.** A malformed, unparseable, or wrong-length input MUST NOT revert and MUST NOT throw. The four signature-verify precompiles (`0x0AE1..0x0AE4`) return a **32-byte zero word** (`0x00..00`, read as "not verified"). The SHAKE256 precompile (`0x0AE5`) returns **empty output** (`0x`). This was verified against the live chain (2026-07-18) and is the consensus rule: every node MUST return byte-identical output for identical input.
- **Output on a well-formed verify.** A **32-byte word**: `0x00..01` if the signature is valid for the given public key and message, `0x00..00` if it is well-formed but cryptographically invalid. Note this is a full 32-byte word, not a single byte. A consuming contract reads the low byte: valid iff `ret.length >= 32 && ret[31] == 0x01` (this is exactly how `AerePQCAttestation._verify` decides).
- **Malformed is indistinguishable from invalid.** Because malformed input returns the same `0x00..00` word as a non-verifying signature, a consuming contract MUST validate field lengths itself and MUST NOT rely on a revert to detect malformed input. Integrating contracts treat a zero word or empty output as "not verified".
- **Pre-activation behavior.** Before block 9,189,161 the band addresses were empty accounts; a `CALL`/`STATICCALL` to any of them succeeded with empty return data (an empty-account call). A consuming contract's length check therefore fails safely on a pre-fork or unforked node, so a stale caller can never record a false positive. Integrators SHOULD gate precompile use on chain height past the activation block, not on probing.
- **Determinism.** Each precompile is a pure function of its input bytes. It reads no chain state, no block context, and no nondeterministic source. Verification is integer-only (Falcon verification uses the integer HashToPoint, integer NTT, and integer norm check; it never touches the floating-point Falcon signing path). All nodes run an identical client build, so all nodes compute the identical result and gas charge.
- **Implementation boundary.** Each precompile is a thin adapter that parses the packed input, invokes the audited Bouncy Castle 1.83 (`bcprov-jdk18on-1.83.jar`) BCPQC verifier already on the client classpath (behind a dedicated classloader-isolation boundary so BC 1.83 cannot collide with the older Bouncy Castle Besu bundles), and encodes the result. There is zero new hand-rolled cryptography. The pure-Solidity verifiers in `aerenew/contracts/contracts/pqc/` are the differential oracle, not the security boundary.
**Authoritative byte-layout source.** The native source of `0x0AE1..0x0AE5` lives in the gold binary (fork commit `c8d2ef9`, "AERE PQC fork baseline (0x0AE1-0x0AE5)"), not in the repository. The byte-exact input layout the live precompiles parse is therefore drawn from the on-chain consuming contract `AerePQCAttestation.sol` (which builds inputs the live precompiles accept, proven by a real on-chain attestation) and the committed conformance KAT vectors. The layouts in Sections 4.2 through 4.4 are as built by `AerePQCAttestation._buildInput` and are the reference a reimplementer should match, with the KAT vectors as the differential oracle.
### 4.2 `0x0AE1` Falcon-512 verify, and `0x0AE2` Falcon-1024 verify
**Address.** `0x0000000000000000000000000000000000000AE1` (Falcon-512), `0x0000000000000000000000000000000000000AE2` (Falcon-1024).
**Scheme.** NIST PQC round-3 Falcon signature verification. Integer-only verification path.
**Parameters:**
| | Falcon-512 (`0x0AE1`) | Falcon-1024 (`0x0AE2`) |
|---|---|---|
| `logn` | 9 | 10 |
| Ring | `Z_q[x]/(x^512 + 1)` | `Z_q[x]/(x^1024 + 1)` |
| Degree `n` | 512 | 1024 |
| `q` | 12289 | 12289 |
| Squared-norm acceptance bound `floor(beta^2)` | 34,034,726 | 70,265,242 |
| Nonce (salt) length | 40 | 40 |
| Public key length | 897 bytes | 1793 bytes |
| Public-key header byte | `0x09` (`0x00 + logn`) | `0x0A` |
| Signature (`esig`) header byte | `0x29` (`0x20 + logn`) | `0x2A` |
**Input calldata layout (as shipped, per `AerePQCAttestation._buildInput`):**
```
input = pk || sm
pk = pkHeader(1) || packed_h ; 897 bytes (Falcon-512), 1793 bytes (Falcon-1024)
; pkHeader = 0x09 (F-512) or 0x0A (F-1024); packed_h = n coefficients, 14-bit packed
sm = sigLen(2, big-endian uint16) || nonce(40) || message || esig
; sigLen == len(esig)
esig = esigHeader(1) || compressedSig ; esigHeader = 0x29 (F-512) or 0x2A (F-1024)
; compressedSig = compressed Gaussian encoding of s2
```
Concretely, `sm` is `abi.encodePacked(uint16(sigLen), nonce, message, esig)`, and the full precompile input is `abi.encodePacked(pk, sm)`. The 40-byte nonce and the `esig` are the two halves of the caller's detached Falcon signature envelope `nonce(40) || esig`.
**Verification behavior (Falcon reference `hash_to_point_vartime` plus norm check):**
1. `HashToPoint`: stream `SHAKE256(nonce || message)` and rejection-sample the challenge polynomial `c` (16-bit big-endian samples, keep values `w < 5q = 61445`, reduce mod `q`), collecting `n` coefficients.
2. Decode the public key into `h` (n coefficients, 14-bit packed; reject coefficients `>= q` and nonzero trailing bits).
3. `comp_decode` the compressed `s2` (sign bit, 7 low bits, unary high bits; reject "-0", overflow above 2047, buffer overrun, nonzero trailing bits, and any unconsumed trailing bytes).
4. Recompute `s1 = c - s2*h` in the ring via negacyclic NTT convolution mod `q`, centered into `(-q/2, q/2]`.
5. Accept iff `||(s1, s2)||^2 <= floor(beta^2)` (34,034,726 for Falcon-512, 70,265,242 for Falcon-1024).
**Output.** 32-byte word: `0x00..01` if valid, else `0x00..00`.
**Fail-closed.** Any structurally malformed input (wrong `pk` length, wrong `pk` header, wrong `esig` header, inconsistent `sigLen`, or a total length that does not frame as `pk || sm`) returns `0x00..00`, identical to a non-verifying signature. It does not revert. (A probe of `0x0AE1` with `0xdeadbeef` returns `0x00..00`, confirmed live 2026-07-18.)
**Gas.**
| | Marginal verify-op gas | Full verify-and-record tx gasUsed | Share of 2^24 cap |
|---|---|---|---|
| Falcon-512 | 40,000 | 86,336 | 0.51% |
| Falcon-1024 | 75,000 | 145,496 | 0.87% |
The marginal verify-op gas is a fixed cost (Falcon verification work is bounded and near-constant per verify). Falcon-1024 does roughly double the ring work of Falcon-512. These figures were measured on the pre-activation scratch fork (chain 28099) and carried to mainnet.
[VERIFY: the precise low-level `sm` byte order the native `0x0AE1`/`0x0AE2` parser expects (whether `message` precedes or follows `esig` inside `sm`), against the gold-binary precompile source or the committed conformance vectors. This spec states the order `sigLen || nonce || message || esig` exactly as `AerePQCAttestation._buildInput` builds it and as a real on-chain attestation confirmed, but the native parser source is not in the repository.]
### 4.3 `0x0AE3` ML-DSA-44 verify
**Address.** `0x0000000000000000000000000000000000000AE3`.
**Scheme.** NIST ML-DSA-44 (Dilithium2), FIPS 204, internal interface (`Verify_internal`, Algorithm 8, `externalMu = false`, empty context string).
**Parameters:** `q = 8380417`, `n = 256`, `(k, l) = (4, 4)`, `d = 13`, `tau = 39`, `gamma1 = 2^17`, `gamma2 = (q-1)/88`, `beta = 78`, `omega = 80`, `lambda = 128`, public key 1312 bytes, signature 2420 bytes, `c~` 32 bytes, infinity-norm acceptance bound `||z||_inf < gamma1 - beta = 130994`.
**Input calldata layout (as shipped):**
```
input = pk || sig || message
pk : 1312 bytes ; rho(32) || t1 (k=4 polys, 10-bit SimpleBitUnpack)
sig : 2420 bytes ; c~(32) || z (l=4 polys, 18-bit BitUnpack) || hint (omega+k = 84 bytes)
message : remaining bytes ; the signed message (32 bytes in the AerePQCAttestation flow)
```
There is **no length-field header**: the fields are fixed-size (1312 and 2420) and the message is the remainder. This is `abi.encodePacked(pubKey, signature, message)` in `AerePQCAttestation._buildInput`.
**Verification behavior.** Full FIPS 204 `Verify_internal`: `pkDecode`; `sigDecode` (`BitUnpack` plus `HintBitUnpack` with strict-increasing and bound validation); `ExpandA` (SHAKE128 rejection sampling of `A_hat` directly in the NTT domain); `mu = H(H(pk, 64) || M, 64)` with `H = SHAKE256`; `c = SampleInBall(c~)`; `w'approx = NTT^{-1}(A_hat o NTT(z) - NTT(c) o NTT(t1 * 2^d))` over `Z_q[x]/(x^256+1)`; `w1 = UseHint(h, w'approx)`; `c~' = H(mu || w1Encode(w1), 32)`; accept iff `||z||_inf < 130994`, the hint is valid, and `c~' == c~`.
**Output.** 32-byte word: `0x00..01` valid, `0x00..00` invalid.
**Fail-closed.** A `pk` length `!= 1312`, a `sig` length `!= 2420`, or any structurally broken input returns `0x00..00` (does not revert). Confirmed live: a probe with `0xdeadbeef` returns `0x00..00`. Note: this precompile implements the *internal* interface (empty context); a general ML-DSA `Verify` with a nonempty context string is a distinct interface and is out of scope for this precompile.
**Gas.** Marginal verify-op gas 55,000; full verify-and-record tx 351,050 (2.09% of the 2^24 cap). This is the change that moves ML-DSA-44 from view-only (its pure-Solidity `verifyAndRecord` is about 52.9M gas, above the cap) to record-on-chain.
### 4.4 `0x0AE4` SLH-DSA-SHA2-128s verify
**Address.** `0x0000000000000000000000000000000000000AE4`.
**Scheme.** NIST SLH-DSA-SHA2-128s (SPHINCS+-SHA2-128s-simple), FIPS 205, internal interface (`slh_verify_internal`, Algorithm 20, no pre-hash).
**Parameters:** `n = 16`, `h = 63`, `d = 7`, `h' = 9`, `a = 12`, `k = 14`, `lg_w = 4`, `w = 16`, `len = 35`, public key 32 bytes, signature 7856 bytes.
**Input calldata layout (as shipped):**
```
input = pk || sig || message
pk : 32 bytes ; PK.seed(16) || PK.root(16)
sig : 7856 bytes ; R(16) || SIG_FORS || SIG_HT
message : remaining bytes
```
No length-field header; fixed-size fields plus the message remainder (`abi.encodePacked(pubKey, signature, message)`).
**Verification behavior.** Full FIPS 205 `slh_verify_internal`: `H_msg = MGF1-SHA256(R || PK.seed || SHA256(R || PK.seed || PK.root || M))` split into `(md, idx_tree mod 2^54, idx_leaf mod 2^9)`; FORS `pkFromSig` with base-`2^12` big-endian index extraction (the FIPS 205 change versus round-3 SPHINCS+); a `d = 7` hypertree of WOTS+ (`len = 35`) with `h' = 9` Merkle layers; tweakable hash `Th = Trunc16(SHA256(PK.seed || 0^48 || ADRSc || M))`. Accept iff the recomputed root equals `PK.root`.
**Output.** 32-byte word: `0x00..01` valid, `0x00..00` invalid.
**Fail-closed.** `pk` length `!= 32`, `sig` length `!= 7856`, or any structurally broken input returns `0x00..00` (does not revert). SLH-DSA is stateless and many-time, so unlike XMSS there is no one-time-per-leaf state caveat at the scheme level.
**Gas.** Marginal verify-op gas 350,000; full verify-and-record tx 558,276 (3.33% of the 2^24 cap). SLH-DSA-SHA2-128s already fit under the cap in pure Solidity (about 1.81M gas), so this precompile is a cost reduction rather than an enabler; it is included so the whole PQC suite shares one native code path and one gas model. The fixed cost is higher than the lattice precompiles because SLH-DSA verification is hash-heavy (thousands of SHA-256 and tweakable-hash calls).
### 4.5 `0x0AE5` SHAKE256
**Address.** `0x0000000000000000000000000000000000000AE5`.
**Scheme.** FIPS 202 `SHAKE256` extendable-output function.
**Input calldata layout (as shipped):**
```
offset 0 : outLen (32 bytes, big-endian word) desired output length in bytes
offset 32 : data (all remaining bytes) message to absorb
```
The live precompile reads `outLen` as a **full 32-byte big-endian word** at offset 0, with the data to absorb starting at offset 32. (An earlier draft framed `outLen` as a 4-byte `uint32`; that is the design record, not the live encoding.)
**Behavior.** Compute `SHAKE256(data)` with rate 136 bytes, domain separation `0x1F`, and `pad10*1` padding (identical to the on-chain `AereFalcon512Verifier.shake256`), then squeeze `outLen` bytes. True extendable-output squeezing: for a fixed `data`, the first `k` bytes of an `outLen = m >= k` output are byte-identical to an `outLen = k` output.
**Output.** Exactly `outLen` bytes. An `outLen` of 0 returns empty output.
**Fail-closed.** Any input the parser cannot interpret as a 32-byte `outLen` word followed by data (including a short input, or the draft 4-byte framing) returns **empty output** (`0x`), not a revert.
Live confirmation (2026-07-18):
- `outLen = 32`, data `abc` returns `0x483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739` = `shake_256(b'abc').hexdigest(32)`.
- `outLen = 32`, empty data returns `0x46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f` = `shake_256(b'').hexdigest(32)`.
- `outLen = 64`, data `abc` returns 64 bytes whose first 32 bytes match the `outLen = 32` result (confirming squeezing).
- The draft 4-byte framing `0x00000020616263` returns `0x` (empty), not parsed under the shipped encoding.
**Gas.** Per-word charge over the data processed plus output, matching the shape of `KECCAK256`:
```
words = ceil((len(data) + outLen) / 32)
gas = SHAKE_BASE + SHAKE_WORD * words
= 60 + 12 * words
```
`SHAKE_BASE = 60` (fixed base, matching the `SHA256` precompile base at `0x02`); `SHAKE_WORD = 12` gas per 32-byte word. A standalone SHAKE256 verify-and-record transaction measured 21,470 gas (0.13% of the cap). An implementation MAY impose an `outLen` upper bound as a chain constant to bound worst-case work.
[VERIFY: whether the live `0x0AE5` enforces an `outLen` upper bound, and if so its value; the design allowed an optional chain-constant bound but the shipped bound (if any) is not sourced in the repository.]
### 4.6 Native EIP-2935 block-hash lookback (write path)
The AerePQC fork activates the native EIP-2935 history-storage system contract.
- **Address.** `0x0000F90827F1C53a10cb7A02335B175320002935`.
- **Structure.** A ring buffer of the last **8191** (`0x1FFF`) block hashes.
- **Write path.** At the start of every block, before any user transaction runs, the parent block hash is written into slot `(number - 1) mod 8191` by a **consensus system call** from the system address. There is no keeper, relayer, or Foundation key; the write is a condition of block validity. In Besu terms this is the `PraguePreExecutionProcessor` system-address write.
- **Read path.** A contract reads a historical hash by `STATICCALL` to the system contract with the 32-byte block number as calldata. A query outside the served window `[number - 8191, number - 1]` reverts (contract-level revert of the read, not a consensus fault).
- **Reach.** At the 500 ms block period, 8191 blocks is approximately 68 minutes of trustless lookback, versus the approximately 128 seconds the 256-block `BLOCKHASH` opcode gives (which remains available and unchanged).
- **Deployment provenance.** The system contract's runtime bytecode is byte-identical to Ethereum's Pectra deployment and was deployed post-genesis via the canonical keyless "Nick's method" transaction `0x67139a552b0d3fffc30c0fa7d0c20d42144138c8fe07fc5691f09c1cce632e15` (mined at block 9,182,379, status 1), so the block-0 state root is unchanged. `eth_getCode` at the address returns the canonical Pectra runtime, which encodes the 8191-block ring buffer.
Nominal Pectra parity lists EIP-2935 from block 2,075,363, but the system contract was not populated on mainnet until the write path shipped in the AerePQC fork at block 9,189,161; from that block the window is live.
### 4.7 EIP-7939 (CLZ opcode)
The AerePQC milestone also activates the EIP-7939 `CLZ` (count leading zeros) opcode. It is a standard Fusaka-era opcode; on Aere it activated at the post-Osaka AerePQC milestone (block 9,189,161) rather than at the Osaka boundary, and the live EIP-2124 fork id folds this in.
[VERIFY: that EIP-7939 CLZ activated at the AerePQC milestone block 9,189,161 specifically, and not at Osaka (block 2,106,606); confirm against the live fork schedule. The Nethermind live-sync fork-id mirror maps it to the post-Osaka boundary.]
### 4.8 Precompiles built but NOT live on mainnet (specified for completeness)
The following three addresses are Aere-specific but are **not activated on mainnet chain 2800**. A mainnet `STATICCALL` to any of them hits an empty account and returns empty. They are specified here so a reimplementer knows the full band allocation and does not accidentally activate them. Their source is in `aerenew/pqc-fork/precompiles/`.
#### 4.8.1 `0x0AE6` ML-KEM-768 deterministic encapsulation (testnet only)
FIPS 203 ML-KEM-768. Aere's first post-quantum confidentiality primitive (every live precompile is signature or hash, giving authenticity only). Built and ACVP-verified (25/25) on an isolated QBFT testnet; founder- and audit-gated for mainnet.
- **Input layout:** `ek(1184) || m(32)` = 1216 bytes exactly. `ek` is the ML-KEM-768 encapsulation (public) key; `m` is the 32-byte encapsulation randomness ("coins").
- **Output layout:** `c(1088) || K(32)` = 1120 bytes; or empty (`0x`) on any malformed input.
- **Behavior:** deterministic Encaps. Feed the caller-supplied `m` as the encapsulation randomness to Bouncy Castle `MLKEMGenerator.internalGenerateEncapsulated(pub, m)`, so every node computes the identical `(c, K)`. (The standard FIPS 203 Encaps draws `m` from a CSPRNG, which cannot run in a consensus-critical precompile; taking `m` from calldata makes it deterministic.)
- **Fail-closed:** `input.size() != 1216`, or any thrown `Throwable`, returns `Bytes.EMPTY`. Source-verified fail-closed in `MLKEM768PrecompiledContract.computePrecompile`.
- **Gas:** fixed `60,000` (`GAS`).
#### 4.8.2 `0x0AE7` Falcon HashToPoint (testnet only)
The SHAKE256-driven map from `(nonce, message)` to a Falcon challenge polynomial `c` in `Z_q[x]/(x^n+1)`, `q = 12289`. Built and KAT-verified (12/12 versus an independent Python FIPS 202 SHAKE256 oracle) on isolated testnet; founder- and audit-gated.
- **Input layout:** `logn(1) || nonce(40) || message(rest)`, where `logn` is 9 (Falcon-512, n=512) or 10 (Falcon-1024, n=1024).
- **Output:** `2n` bytes, that is `n` coefficients each a big-endian `uint16` in `[0, q)`.
- **Behavior:** absorb `nonce || message` into a SHAKE256 sponge; repeatedly squeeze two bytes, interpret as a big-endian 16-bit `w`, keep `w mod q` whenever `w < 5q = 61445`, until `n` coefficients are collected (matches the reference `hash_to_point_vartime`).
- **Fail-closed:** input length `< 41`, or `logn` not in `{9, 10}`, returns `Bytes.EMPTY`; any thrown `Throwable` returns `Bytes.EMPTY`.
- **Gas:** `BASE_GAS(60) + GAS_PER_WORD(12) * words`, where `words = ceil(absorbBytes/32) + ceil(squeezeBytes/32)`, `absorbBytes = input.size() - 1`, and `squeezeBytes = (2 * n * 70) / 64` (a conservative fixed approximately 1.094x expansion so gas is a pure function of the input, independent of the rejection-sampling outcome). On malformed input (`n == 0`) it charges only `BASE_GAS + GAS_PER_WORD * ceil(input.size()/32)`.
#### 4.8.3 `0x0AE8` hash-based STARK verify (reference skeleton, never to be activated as-is)
A reference skeleton for direct on-chain verification of a hash-based FRI/STARK proof. **It is not a working verifier.** Scope caveat (2026-07-19 research finding): the generic components confirmed so far are a BabyBear + FRI STARK verifier, conformance-confirmed against Plonky3 `0.4.3-succinct`, that targets Aere's OWN Plonky3 circuits, NOT SP1 6.1.0. The pinned SP1 6.1.0 is a Hypercube release (KoalaBear multilinear: BaseFold plus sumcheck-zerocheck plus LogUp-GKR), so FRI does not apply to it and this skeleton does not verify SP1 6.1.0 proofs; removing the SP1 BN254 Groth16 outer wrap requires a separate ~22 to 32 person-week retarget to the SP1 Hypercube stack (see `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`). The crypto core (Poseidon2-BabyBear permutation, FRI folding arithmetic, SP1 recursion-AIR constraint evaluation) is deliberately not ported; those methods return "unavailable", which makes `computePrecompile` fail-closed: it returns `Bytes.EMPTY` for every input and can never return a positive result by construction. It MUST NOT be activated on mainnet until the crypto core is ported and audited.
- **Wire layout (v1):** `magic(4)="AS1\0" || version(1) || configId(1) || reserved(2) || vkeyDigest(32) || publicValuesLen(4, big-endian) || publicValues || proofLen(4, big-endian) || friProof`.
- **Output:** on a fully verified proof, the 32-byte word `0x00..01`; on any malformed input, unsupported config, or (in this skeleton) any path reaching the un-ported core, empty (`0x`). Never faults.
- **Gas:** illustrative placeholder model `BASE_GAS(250,000) + numQueries * GAS_PER_QUERY(40,000) + GAS_PER_BYTE(3) * input.size()`, to be re-benchmarked on the frozen artifact.
### 4.9 Fork-registration summary (for a reimplementer)
The five live precompiles are registered in `MainnetPrecompiledContracts.populateForFutureEIPs` (the `futureEips` milestone), gated by `futureEipsTime = 1783820272` (block 9,189,161). The two testnet precompiles add their `Address` constants (`AERE_MLKEM768`, `AERE_HASHTOPOINT`) and register into the same `populateForFutureEIPs` method in their patch, which is the pattern the mainnet five follow. Address band allocation:
| Address | Precompile | Standard | Live on mainnet? |
|---|---|---|---|
| `0x0AE1` | Falcon-512 verify | NIST round-3 Falcon | Yes (block 9,189,161) |
| `0x0AE2` | Falcon-1024 verify | NIST round-3 Falcon | Yes |
| `0x0AE3` | ML-DSA-44 verify | FIPS 204 (internal) | Yes |
| `0x0AE4` | SLH-DSA-SHA2-128s verify | FIPS 205 (internal) | Yes |
| `0x0AE5` | SHAKE256 | FIPS 202 XOF | Yes |
| `0x0AE6` | ML-KEM-768 encapsulation | FIPS 203 | No (testnet only) |
| `0x0AE7` | Falcon HashToPoint | Falcon / FIPS 202 | No (testnet only) |
| `0x0AE8` | Hash-based STARK verify (skeleton; BabyBear/FRI, targets Aere's own Plonky3 circuits, NOT SP1 6.1.0 which is Hypercube; see 4.8.3) | Plonky3 BabyBear FRI/STARK | No (reference skeleton) |
The band `0x0AE1..0x0AE5` sits above the Ethereum standard precompiles, above the EIP-2537 BLS12-381 range, and above the RIP-7951 P-256 precompile at `0x100`, so upstream precompile allocations do not collide. If upstream Ethereum later allocates into this band, Aere will re-evaluate to preserve equivalence.
### 4.10 Consuming-contract note (not part of the precompile, but load-bearing for correct use)
A `0x00..01` from a verify precompile establishes only that the signature is valid for the given public key and message. It does not establish freshness, ordering, replay protection, or key ownership. Consuming contracts add their own nonce and domain separation. `AerePQCAttestation` (`0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A`), for example, supplies the 32-byte message itself and binds a per-key strictly increasing nonce, so a relayer can only pay gas and can never forge or replay. For hash-based schemes with one-time-per-leaf state (XMSS, WOTS+, which are pure-Solidity, not precompiles), a valid verification does not prove the signer has not reused a leaf; that state is the application's responsibility.
---
## 5. Fee and burn mechanics
Aere makes two distinct, deliberately separated changes in the fee area. One is a protocol-layer base-fee floor. The other is an application-layer burn. **They must not be conflated, and neither is a consensus base-fee burn.**
### 5.1 The 1-Gwei EIP-1559 base-fee floor (protocol layer)
- **What it is.** A minimum on the EIP-1559 base fee: from block **10,141,734**, the base fee on chain 2800 cannot fall below **1 Gwei = 1,000,000,000 wei**. Before that block the real minimum floored at 7 wei.
- **Where it lives.** A single-file, fork-gated clamp in the fee-market logic (`LondonFeeMarket.java`), gated by client flags `aere.basefee.floor.forkBlock` and `aere.basefee.floor.value = 1000000000`. Both flags are dormant by default, so the build is byte-identical to the pre-floor binary before the fork block. The `BASEFEE` opcode (EIP-3198) returns this floored value.
- **What it is not.** It is not a burn. It sets a minimum base fee; it does not change base-fee disposal. It is entirely separate from the burn in Section 5.2.
- **Consensus impact.** None that can halt or split the chain: the QBFT header ruleset on chain 2800 lacks a base-fee gas-price validation rule (Section 3.6), so non-validators follow automatically and validators need the change only for base-fee consistency across proposers.
Activation was clean: base fee 7 wei at block 10,141,733 becomes 1 Gwei at 10,141,734 and stays 1 Gwei; the fork-block hash was identical across the public RPC and the second RPC (no split); N = 7 held with no halt. (Source: memory `rpc_basefee_honesty_fix`; runbook `aerenew/docs/BASEFEE-FLOOR-FORK-RUNBOOK-2026-07-17.md`.)
[VERIFY: EIP-1559 base-fee *disposal* on chain 2800, that is whether the 1559 base fee is protocol-burned as on Ethereum or credited. The documented Aere "burn" is the separate validator-reward cut of Section 5.2, so the base-fee disposal itself should be stated precisely before publication.]
### 5.2 The validator-reward-cut burn (application layer)
Aere's "burn" is a cut of the validator coinbase block reward, applied *above consensus* by a contract that validators (or their forwarder daemons) call each block. It is **not** an EIP-1559 protocol-level base-fee burn.
**Splitter: `AereCoinbaseSplitterV2`.** A 3-way split of the validator coinbase reward, in basis points (`BPS = 10000`):
| Bucket | Default | Cap | Destination |
|---|---|---|---|
| `burnBps` | 3750 (37.5%) | `MAX_BURN_BPS = 5000` (50%) | `AereFeeBurnVault` (direct burn) |
| `sinkBps` | 1500 (15.0%) | `MAX_SINK_BPS = 3000` (30%) | `AereSink` (3-bucket router) |
| `rebateBps` | 4750 (47.5%) | derived: `10000 - burnBps - sinkBps` | back to the validator |
Rules a reimplementer must honor:
- The rate is Foundation-settable via `setBps(_burnBps, _sinkBps)` but hard-bounded: `setBps` reverts if `_burnBps > MAX_BURN_BPS` (5000), if `_sinkBps > MAX_SINK_BPS` (3000), or if `_burnBps + _sinkBps > 10000`. The burn can never exceed 50%.
- The splitter cannot custody AERE across calls: every `splitAndDistribute` / `splitToSelf` fully dispatches `msg.value` into the three buckets in one transaction. The sink bucket is wrapped via WAERE and routed through `IAereSink.flush()` atomically; if the sink call reverts, the whole transaction reverts and the validator retries.
- The sink address is owner-settable but rate-limited by a 7-day timelock (`SINK_CHANGE_TIMELOCK`). Until a sink is set, the splitter behaves like V1 (no sink bucket; the 15% would be rebated instead).
**Router: `AereSink`.** An immutable, ownerless 3-bucket router. Its invariants (verified in tests, `saere_architecture` design):
- No owner, no admin, no governance role; no `setBucket`, `setRecipient`, `setRouter`, or `pause`.
- Bucket recipients (`BURN_VAULT`, `sAERE_VAULT`) set at deploy, immutable.
- Bucket basis-point splits set at deploy, immutable, and the constructor reverts unless they sum to exactly 10000.
- The three buckets are: (1) BURN (extra burn at the burn vault), (2) BUYBACK-AND-BURN (non-AERE fees swapped to AERE and burned), (3) STAKER-YIELD (transferred to the sAERE ERC-4626 vault, lifting the exchange rate).
- `AereSink` does not custody funds across calls; every `flush(token, amount)` fully dispatches into the three buckets. Residual dust (up to 2 wei per call from integer rounding) is re-flushable by a permissionless `sweepDust()`.
**Burn vault: `AereFeeBurnVault`.** No withdraw function and no admin escape hatch; value routed here is unrecoverable by construction.
**Why this is not a base-fee burn (design record).** Routing the burn through the coinbase layer rather than enshrining it in consensus keeps the burn out of the consensus-critical path (a burn-rate change cannot halt or fork the chain, whereas changing a consensus base-fee rule would be a hard fork), and it lets the *destinations* be made immutable at the contract layer while the *rate* stays a bounded, governable dial. What is sealed is not the rate but the endpoints. This document must not describe the Aere burn as a base-fee burn; it is a validator-reward-cut burn, rate-adjustable within a 50% ceiling, into immutable destinations.
---
## 6. Deliberate EIP deviations from Ethereum parity
Aere tracks Ethereum's ruleset at Pectra plus Fusaka, so its EVM surface matches mainnet at the same addresses and opcodes, with one set of intentional deviations that follow from Aere being a QBFT chain with no separate beacon (consensus) layer and no blob data-availability market. A reimplementer must reproduce each of these deviations to match chain 2800. (Source: `AERE-EIP-COMPATIBILITY-MATRIX`.)
Status vocabulary: **Supported** (matches Ethereum), **Partial** (present but materially limited), **Not implemented** (absent), **No-op** (present in the ruleset but neutralized, returns a defined zero/empty value under QBFT).
| EIP | Name | Aere status | Deviation a reimplementer must reproduce |
|---|---|---|---|
| 1559 | Fee market | Partial | Fee-market mechanics present, plus the 1-Gwei base-fee floor from block 10,141,734 (Section 5.1). No consensus base-fee burn narrative; the "burn" is the separate validator-reward cut (Section 5.2). |
| 3198 | BASEFEE opcode | Supported | Returns the current base fee, floored at 1 Gwei. |
| 4844 | Blob transactions | Partial | Type-3 blob transactions exist in the EVM, but Aere runs no blob data-availability market; blobs carry no fee and are not served or persisted. |
| 7516 | BLOBBASEFEE opcode | No-op | Returns 0. There is no blob market, so no blob base fee. |
| 4788 | Beacon block root in the EVM | No-op | The beacon-root ring buffer returns `0x0`. A QBFT chain has no separate beacon layer to source a root from. |
| 4895 | Beacon push withdrawals | No-op | The withdrawal-operation ruleset is present, but Aere has no beacon layer to originate withdrawals, so none are produced. |
| 2935 | Historical block hashes | Supported (activated at AerePQC fork) | The 8191-block ring buffer at `0x0000F90827...2935`, populated on mainnet from block 9,189,161, not from Pectra (Section 4.6). |
| 6110 | Validator deposits on chain | Partial | The deposit-request ruleset is tracked for Pectra parity, but no beacon layer consumes deposits; a deposit does not create or fund a QBFT validator (the set is QBFT-managed). |
| 7002 | EL triggerable withdrawals/exits | Partial | Ruleset present, no beacon consumer; does not affect the QBFT validator set. |
| 7251 | MAX_EFFECTIVE_BALANCE consolidations | Partial | Ruleset present, no beacon consumer. |
| 7685 | General execution-layer requests | Partial | The requests umbrella (6110/7002/7251) exists in the ruleset, but no beacon layer consumes the requests. |
| 7549 | Move committee index outside signature | Not implemented | Beacon consensus-layer change only; no EVM surface; not applicable under QBFT. |
| 3074 | AUTH / AUTHCALL | Not implemented | Deliberately absent; superseded by EIP-7702, which is the EOA-delegation primitive Aere carries. |
| 7702 | Set EOA account code | Supported | Matches Ethereum. The reason 3074 is not needed. |
| 7825 | Per-transaction gas cap (2^24) | Supported | Enforced on chain 2800; the constraint that motivates the PQC precompiles. |
| 7939 | CLZ opcode | Supported | Activated at the AerePQC milestone (block 9,189,161), not at Osaka (Section 4.7). |
| 7594 | PeerDAS | Not implemented | No data-availability sampling role under QBFT (no blob market). |
| 7805 | FOCIL (inclusion lists) | Not implemented | No fork-choice or proposer-inclusion layer to enforce inclusion lists against under QBFT. |
| 7692 | EOF (EVM Object Format) | Not implemented | Not activated under the current ruleset; the `0xEF` lead byte stays reserved by EIP-3541 (legacy behavior). |
**Deviations recap (the seven that matter):**
1. **Base-fee floor plus a validator-reward burn, not a consensus base-fee burn** (Sections 5.1, 5.2).
2. **EIP-4844 blobs present, EIP-7516 BLOBBASEFEE returns 0** (no blob market).
3. **EIP-4788 parent beacon block root returns `0x0`** (no beacon layer).
4. **EIP-3074 not implemented, superseded by EIP-7702**.
5. **PeerDAS (7594), FOCIL (7805), and EOF (7692) have no active role under QBFT** (0xEF stays reserved by EIP-3541).
6. **Validator-lifecycle requests (6110/7002/7251/7685) have no beacon consumer** (validator set is QBFT-managed).
7. **EIP-2935 became functionally live at the AerePQC fork (block 9,189,161), not at Pectra**.
[VERIFY: that the AerePQC `futureEips` milestone (futureEipsTime = 1783820272, block 9,189,161) does NOT enable the EOF opcode set or EIP-3540/3670 container validation. Corroborating evidence (served genesis config, the Nethermind fork-id mirror, and the engineering spec) indicates EOF is off, but this milestone rides Besu's `futureEips` key, which in stock Besu is EOF's staging ground, so the fork build's spec definition should be inspected to close the loop.]
[VERIFY: whether the validator-lifecycle request system contracts (6110/7002/7251) are actually populated on mainnet 2800 and whether mainnet block headers carry a non-empty requests hash. Regardless of the outcome, no beacon layer consumes these requests, so the "Partial" status and the no-effect-on-validator-set caveat hold.]
**Additions beyond Ethereum (recap).** Besides the deviations, Aere adds callable addresses not in the base Ethereum mainnet precompile set: the live PQC band `0x0AE1..0x0AE5` (Section 4), the non-live `0x0AE6..0x0AE8` (Section 4.8), and the RIP-7951 / RIP-7212 P-256 precompile at `0x100` (approximately 3,450 gas, active from the Osaka ruleset; shared with Ethereum's Osaka, so standards-aligned rather than Aere-proprietary). None of these make Aere consensus post-quantum.
---
## 7. How to reimplement Aere's delta on a stock client
This is the minimal, ordered checklist a stock Besu (or Nethermind) operator applies to match Aere Network on chain 2800. Each item points to the section that specifies it.
### 7.1 Chain configuration (genesis and forks)
1. **Chain ID** `2800`. (Section 2.1.)
2. **Genesis allocation**: the six balances totaling 2.8B AERE at the exact addresses in Section 2.1.
3. **Genesis fields**: `gasLimit = 0x1fffffffffffff`, `difficulty = 0x1`, `timestamp = 0x69fbd960`, the genesis `coinbase = 0x0`, and the genesis QBFT `extraData` encoding the initial three-validator set (Section 3.2). These must be byte-exact to reproduce the block-0 hash.
4. **QBFT config**: `blockperiodseconds = 1`, `epochlength = 30000`, `requesttimeoutseconds = 4`, plus the transition `{ "block": 2138451, "xblockperiodmilliseconds": 500 }`. (Sections 2.2, 2.4.)
5. **Fork schedule**: `londonBlock = 0`, `shanghaiTime = 0`, `cancunTime = pragueTime = 1780189051`, `osakaTime = 1780220351`, `futureEipsTime = 1783820272`. (Section 2.5.)
6. **Enforce EIP-7825** (per-transaction cap 2^24), identical to Fusaka.
### 7.2 Client code changes (the AerePQC fork)
7. **Register five precompiles** at `0x0AE1..0x0AE5` in the `futureEips` precompile set, each wrapping the Bouncy Castle 1.83 BCPQC verifier for its scheme, behind a classloader-isolation boundary. Match the exact input parsing (Sections 4.2 to 4.5), the fixed gas per scheme, and the fail-closed behavior (32-byte zero word for `0x0AE1..0x0AE4`, empty for `0x0AE5`; never revert).
8. **Native EIP-2935 write path**: at block start, before user transactions, write the parent hash into slot `(number - 1) mod 8191` from the system address; serve reads within `[number - 8191, number - 1]`; ensure the history contract runtime at `0x0000F90827...2935` is the canonical Pectra runtime (deploy it via the keyless Nick's-method transaction so the block-0 state root is unchanged). (Section 4.6.)
9. **Activate EIP-7939 CLZ** at the same milestone. (Section 4.7.)
10. **Do NOT** activate EOF, PeerDAS, or FOCIL; keep `0xEF` reserved by EIP-3541; keep BLOBBASEFEE and the beacon-root buffer returning 0. (Section 6.)
### 7.3 Client code change (the base-fee floor)
11. **Add a fork-gated base-fee floor** in the fee-market logic: from block 10,141,734, clamp the EIP-1559 base fee to a minimum of 1,000,000,000 wei (1 Gwei). Keep it dormant (byte-identical to stock) before the fork block. (Section 5.1.) Roll it out across all validators for base-fee consistency; it cannot halt the chain.
### 7.4 Application layer (not a client change)
12. **Deploy the burn stack** and point the validators' coinbase forwarder daemon at `AereCoinbaseSplitterV2` (3-way split: `burnBps` default 3750 capped at 5000, `sinkBps` default 1500 capped at 3000, `rebateBps` derived), routing the sink bucket through the immutable `AereSink` and the burn bucket to the no-withdraw `AereFeeBurnVault`. (Section 5.2.) This is an off-chain operational wiring plus contracts, not a consensus rule.
### 7.5 What you do NOT change
13. The EVM instruction set (except CLZ), the gas schedule (except the precompile additions and the base-fee floor), transaction types, the state trie, RLP, receipts, logs, and JSON-RPC are all stock Pectra-plus-Fusaka. Do not modify them.
14. Consensus stays classical secp256k1 ECDSA QBFT. Do not add any post-quantum consensus rule; the precompiles are an application and account layer verification surface only.
A client built to this checklist validates the live chain, re-executes chain-2800 blocks to byte-identical state roots, and runs the five PQC precompiles byte-for-byte. The reference second client (patched Nethermind 1.39.0) is exactly such a build and has re-validated the full chain to genesis and re-executed real blocks to identical hashes and state roots, and runs the 27/27 PQC KAT vectors identically.
---
## 8. Consolidated [VERIFY] register
Every flag left open for a human to confirm before external publication:
1. **[Section 2.3] Operative block gas limit** on the live head. Genesis declares `0x1fffffffffffff` (2^53 1), but Besu block-gas-limit dynamics may have moved the operative header `gasLimit`; confirm against `eth_getBlockByNumber("latest")`.
2. **[Section 2.4] Inline fork/transition fields in the running config.** `aere-genesis-current.json` in the repo lacks the block-period transition and the Cancun/Prague/Osaka/futureEips time fields; confirm the authoritative running config carries them.
3. **[Section 2.5] Pectra and Osaka block heights** (2,075,363 / 2,106,606), which are timestamp-gated, against the live head history.
4. **[Section 3.2] The seven live validator addresses** at the current head (only the three genesis validators are recoverable from the genesis `extraData`).
5. **[Section 4.2] The low-level `sm` byte order** the native `0x0AE1`/`0x0AE2` parser expects, against the gold-binary precompile source or the conformance vectors (this spec uses the order `AerePQCAttestation._buildInput` builds).
6. **[Section 4.5] Any `outLen` upper bound** enforced by the live `0x0AE5` SHAKE256 precompile.
7. **[Section 4.7] EIP-7939 CLZ activation height** (AerePQC milestone block 9,189,161 versus Osaka block 2,106,606).
8. **[Section 5.1] EIP-1559 base-fee disposal** on chain 2800 (protocol-burned versus credited), stated precisely and kept distinct from the Section 5.2 burn.
9. **[Section 6] EOF off under `futureEips`.** Confirm the AerePQC milestone does not enable the EOF opcode set or EIP-3540/3670 container validation, since it rides Besu's `futureEips` key (EOF's usual staging ground).
10. **[Section 6] Validator-lifecycle request contracts (6110/7002/7251/7685)** populated on mainnet and whether headers carry a requests hash (status and no-beacon-consumer caveat hold regardless).
---
## Appendix A. Activation timeline (chain 2800)
| Event | Block | Time gate | What changed |
|---|---|---|---|
| Genesis-v2 | 0 | `timestamp 0x69fbd960` | Current chain genesis, 2.8B supply, 3 genesis validators, London + Shanghai from block 0. |
| Pectra (Cancun + Prague) | 2,075,363 | `cancunTime = pragueTime = 1780189051` | EVM at Pectra parity (EIP-7702, EIP-2537, transient storage, etc.). EIP-2935 listed but not yet populated. |
| Osaka (Fusaka) | 2,106,606 | `osakaTime = 1780220351` | EIP-7825 per-tx cap, RIP-7951 P-256 at `0x100`. |
| Block-period transition | 2,138,451 | QBFT `xblockperiodmilliseconds` | 1000 ms to 500 ms block period. |
| EIP-2935 history contract deploy | 9,182,379 | Nick's-method keyless tx | History contract runtime populated (pre-write-path). |
| AerePQC hard fork | 9,189,161 | `futureEipsTime = 1783820272` | PQC precompiles `0x0AE1..0x0AE5`, EIP-2935 write path, EIP-7939 CLZ. |
| Falcon-1024 on-chain attestation | 9,200,542 | (application) | First real Falcon-1024 verify recorded through `0x0AE2` (tx `0xb659...9ec1`, status 1, gasUsed 692,206). |
| Base-fee floor | 10,141,734 | `aere.basefee.floor.forkBlock` | 1-Gwei EIP-1559 base-fee floor. |
## Appendix B. Address registry (Aere-specific, live on mainnet)
| Address | What |
|---|---|
| `0x0AE1` | Falcon-512 verify precompile |
| `0x0AE2` | Falcon-1024 verify precompile |
| `0x0AE3` | ML-DSA-44 verify precompile (FIPS 204 internal) |
| `0x0AE4` | SLH-DSA-SHA2-128s verify precompile (FIPS 205 internal) |
| `0x0AE5` | SHAKE256 precompile (FIPS 202) |
| `0x100` | P-256 / secp256r1 verify (RIP-7212 / RIP-7951; shared with Ethereum Osaka) |
| `0x0000F90827F1C53a10cb7A02335B175320002935` | EIP-2935 history-storage system contract (8191-block ring buffer) |
| `0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A` | AerePQCAttestation (consuming contract; input-layout oracle) |
Non-live (specified in Section 4.8, not activated on mainnet): `0x0AE6` (ML-KEM-768, testnet), `0x0AE7` (Falcon HashToPoint, testnet), `0x0AE8` (hash-based STARK verify, reference skeleton; BabyBear/FRI, targets Aere's own Plonky3 circuits, NOT SP1 6.1.0 which is Hypercube, see 4.8.3).
## Appendix C. Build artifact of record
- Base client: `hyperledger/besu:26.4.0`, base commit `d203201`.
- Fork commits: `c8d2ef9` ("AERE PQC fork baseline (0x0AE1-0x0AE5)"), and `cc6bf56` (the two testnet precompiles `0x0AE6`/`0x0AE7`, not on mainnet).
- Gold binary SHA-256: `7c5c0743088e9fa765d54c745814601b3c7a27a892e396156f88092b34c80772`.
- Primitive library: Bouncy Castle `bcprov-jdk18on-1.83.jar` (Falcon, ML-DSA, SLH-DSA, SHAKE, ML-KEM), classloader-isolated on the client classpath.
- Second client: patched Nethermind 1.39.0 (.NET), validation cross-check and PQC KAT re-runner; not a live producing validator.
---
*End of specification. The post-quantum scope boundary is absolute: signatures, accounts, and the `0x0AE1..0x0AE5` precompiles verify post-quantum schemes; consensus (secp256k1 ECDSA QBFT) and the on-chain ZK verifiers (BN254 Groth16) are classical. Aere Network as a whole is not post-quantum, and no material derived from this document may claim otherwise.*

View File

@ -0,0 +1,91 @@
# Aere Network Consolidation QA Report
Date: 2026-07-19
Scope: the ~12 new Solidity contracts plus their tests added during the build loop, and a cross-document consistency check over the new AERE-*.md docs.
Method: read-and-run audit only. No contract or test was modified. Results below are the real command outputs.
---
## PART 1: COMPILE THE WHOLE SUITE
Command: `cd aerenew/contracts && npx hardhat clean && npx hardhat compile`
(clean was run first, because a plain `compile` reported "Nothing to compile" from cache; the numbers below are from a genuine full rebuild.)
VERDICT: GREEN. `Compiled 339 Solidity files successfully (evm targets: cancun, paris).`
All of the new contracts are present in the tree and compiled with zero errors:
- contracts/oracle/AereRandomnessBeaconV2.sol
- contracts/AereRecoveryRegistry.sol
- contracts/erc8004/ (IERC8004.sol, AereIdentityRegistry8004.sol, AereReputationRegistry8004.sol, AereValidationRegistry8004.sol, AereAP2MandateVerifier.sol)
- contracts/mpc/AerePQAggregateVerifier.sol
- contracts/modular/AerePQAggregateModule.sol
- contracts/compliance/AereTrustRegistry.sol, AereVerifiableCredential.sol, AereBitstringStatusList.sol
- contracts/intents/AereDestinationSettler.sol
- contracts/depin/AereComputeMarketV3.sol
- contracts/agentic/AereVectorStore.sol
- contracts/pqc/AereAccountMigrator.sol
- contracts/pqfinality/AerePQAttestationKeyRegistry.sol, AereFinalityCertificateVerifier.sol
Compiler warnings only (no errors), all pre-existing and non-blocking:
- Transient storage (EIP-1153) advisory notes in AereCancunCanary.sol (reentrancy-guard pattern, benign).
- Declaration-shadowing notes (constructor param vs same-named getter) in AereGovernanceStaked.sol, agentic/AereAIReputation.sol, agentic/AereAgentBond.sol.
- Shift-result-type advisory in pqc/AereFalcon1024Verifier.sol, AereFalcon512Verifier.sol, AereMLDSA44Verifier.sol.
- Unused-parameter notes in zkverify/AereHalo2CubicVerifier.sol.
- Mutability-can-be-pure note in AereConsensus.sol.
None of these touch the new contracts under audit except cosmetically, and none block compilation.
---
## PART 2: NEW TEST FILES (run one at a time)
Each file run with `npx hardhat test test/<file>`. AereRandomnessBeaconV2 was run with the prague config as instructed (`--config hardhat.config.prague.js`); EIP-2537 was present in that EVM, so the real pairing path ran.
| # | Test file | Passing | Pending | Failing |
|---|-----------|---------|---------|---------|
| 1 | test/AereRecoveryRegistry.test.js | 10 | 0 | 0 |
| 2 | test/erc8004-adapters.test.js | 18 | 0 | 0 |
| 3 | test/AerePQAggregate.test.js | 17 | 0 | 0 |
| 4 | test/aere-pq-screen.test.js | 12 | 0 | 0 |
| 5 | test/destination-settler.test.js | 4 | 0 | 0 |
| 6 | test/AereRandomnessBeaconV2.test.js (prague config) | 15 | 1 | 0 |
| 7 | test/AereComputeMarketV3.test.js | 13 | 0 | 0 |
| 8 | test/aere-vector-store.test.js | 8 | 0 | 0 |
| 9 | test/account-migrator.test.js | 9 | 0 | 0 |
| 10 | test/AerePQFinalityCertificate.test.js | 12 | 0 | 0 |
| | GRAND TOTAL | 118 | 1 | 0 |
VERDICT: GREEN. 118 passing, 1 pending, 0 failing across all 10 new test files.
Note on the 1 pending (file 6): it is the deliberately-skipped "without EIP-2537, a genuine sig is also rejected (never stub-true)" fail-closed-absence check. The harness prints "EIP-2537 present: this fail-closed-absence check is not applicable" and marks it pending because the prague EVM DOES have EIP-2537 (real BLS12-381 pairing verification, "YES"). This is expected behavior, not a failure: the positive pairing-verification tests all ran and passed on the real precompile path.
---
## PART 3: CROSS-DOCUMENT CONSISTENCY CHECK
Grepped across the new AERE-*.md docs in aerenew/docs/. Findings against each invariant:
1. Burn = 37.5% of the validator reward, cap 50%, NOT a base-fee burn.
CONSISTENT. Every doc that mentions the burn describes it as a cut of the validator coinbase block reward, `burnBps` default 3750 (37.5%), hard-capped at `MAX_BURN_BPS = 5000` (50%), applied above consensus by `AereCoinbaseSplitterV2`, explicitly not an EIP-1559 protocol base-fee burn. See AERE-ENGINEERING-SECURITY-SPEC.md:205-217, AERE-PROTOCOL-SPECIFICATION.md:497-540, AERE-EIP-COMPATIBILITY-MATRIX.md:109, AERE-AIP-PROCESS-AND-INDEX.md:110/137, AERE-CERTORA-FORMAL-VERIFICATION.md:170/187-188. No contradiction. (Several docs carry an honest `[VERIFY]` marker asking that EIP-1559 base-fee DISPOSAL be stated precisely; that is an open verification item, and it is careful to keep disposal distinct from the validator-reward-cut burn, so it does not contradict the invariant.)
2. Live precompiles 0x0AE1..0x0AE5 (mainnet, block 9,189,161); 0x0AE6/0x0AE7 testnet-only; 0x0AE8 non-working skeleton.
CONSISTENT. Uniform across AERE-PROTOCOL-SPECIFICATION.md (4.2-4.8, tables at 480-487, 653-670), AERE-EIP-COMPATIBILITY-MATRIX.md:91-98, AERE-BENCHMARK-REPORT.md:253-264, AERE-ARCHITECTURE-MAP.md:43-44, AERE-NIST-VALIDATION-STATUS.md:28/90, AERE-MIGRATION-TOOLKIT.md:87. 0x0AE1-0x0AE5 are consistently "live on mainnet 2800, activated block 9,189,161"; 0x0AE6 (ML-KEM-768) and 0x0AE7 (Falcon HashToPoint) are consistently "testnet only, founder/audit-gated, not on mainnet"; 0x0AE8 (hash-based STARK verify) is consistently "reference skeleton, non-working, never to be activated as-is." No contradiction. Scope caveat (2026-07-19 finding): 0x0AE8's confirmed generic components verify BabyBear + FRI STARKs (Aere's OWN Plonky3 circuits), NOT SP1 6.1.0, which is Hypercube (KoalaBear multilinear); docs that still label 0x0AE8 "SP1 inner STARK verify" carry the corrected scope. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
3. Consensus params N=7, f=2, quorum 5 of 7.
CONSISTENT. Stated identically everywhere it appears: AERE-ENGINEERING-SECURITY-SPEC.md:37/138/227 ("N=7 validators, f=2, commit quorum 2f+1 = 5 of 7"), AERE-AIP-PROCESS-AND-INDEX.md:166, AERE-EIP-COMPATIBILITY-MATRIX.md:3, AERE-CROSS-CLIENT-DETERMINISM.md:93/373, AERE-ERC7683-DESTINATION.md:113-114. The live set is consistently N=7. N=9 appears only as a forward-looking target inside the fault matrix (AERE-FAULT-INJECTION-HARNESS.md:57-59) and is clearly labeled a design/target configuration, not the live set. No contradiction.
4. PQC scope boundary (consensus is classical ECDSA QBFT; only signatures/accounts/precompiles are post-quantum; PQ consensus is NOT live).
CONSISTENT and repeatedly reinforced, never contradicted. AERE-ARCHITECTURE-MAP.md:5 leads with it ("consensus today is classical ECDSA (secp256k1) QBFT ... There is no post-quantum consensus live on mainnet"). Echoed in AERE-AIP-PROCESS-AND-INDEX.md:148/277, AERE-CERTORA-FORMAL-VERIFICATION.md:25, AERE-ENGINEERING-SECURITY-SPEC.md:58/313, AERE-ERC8004-SUMMARY.md:53-54, AERE-ERC8004-X402-CONFORMANCE.md:22-23, AERE-ERC7683-DESTINATION.md:179-181, AERE-COMPUTE-MARKET-V3.md:86, AERE-MIGRATION-TOOLKIT.md:74, AERE-PQ-SCREEN-SUMMARY.md:58, AERE-EIP-COMPATIBILITY-MATRIX.md:101, AERE-CROSS-CLIENT-DETERMINISM.md:66/351. The fault-injection harness (AERE-FAULT-INJECTION-HARNESS.md:4/9) explicitly keeps the PQ-consensus flip "founder-gated and audit-gated," i.e. not live. No doc implies post-quantum consensus is live.
5. 273k TPS labeled a ceiling, never a live/measured rate.
CONSISTENT. AERE-BENCHMARK-REPORT.md:42/49/55/61/81/372 repeatedly calls it an "architectural ceiling / design maximum / theoretical maximum," explicitly states "There is no testnet measurement that validates 273,000 TPS for L1," and separates it from the Block-STM harness figure (~247k-270k at gas=80, called a coincidence not a validation). AERE-REPRODUCIBLE-BUILDS.md:244 states "273,000 TPS is a synthetic design ceiling, not a measured mainnet number." No doc presents it as a live or measured rate. No contradiction.
CONSISTENCY VERDICT: No contradictions found on any of the five invariants. (The docs contain a number of honest `[VERIFY]` / `[MEASURE]` markers flagging open verification items, e.g. EIP-1559 base-fee disposal, EIP-7939 CLZ activation height, EOF-off confirmation. These are self-disclosed open items, not internal contradictions.)
---
## SUMMARY
- Compile: GREEN. 339 files compiled successfully, all new contracts present, warnings only.
- Tests: GREEN. 118 passing, 1 pending (expected EIP-2537-present skip), 0 failing across all 10 new test files.
- Consistency: no contradictions on burn, precompile bands, consensus params, PQC scope boundary, or the 273k TPS ceiling framing.

View File

@ -0,0 +1,62 @@
# Spec #14: Account migration + network key rotation (quantum layer 11)
Built the LAST account-layer quantum-resistance piece: a real, no-custody,
tested contract for moving assets off a classical ECDSA EOA onto a post-quantum
account, plus a design doc covering both that and network key rotation (gated).
## Deliverables
1. Contract: `aerenew/contracts/contracts/pqc/AereAccountMigrator.sol` (solc 0.8.23,
OZ SafeERC20 + ReentrancyGuard). New file; deployed contracts untouched.
- `migrate(destination, tokens[], amounts[], moveNative)`: atomic sweep of ERC-20
balances + optional native AERE from caller (old EOA) to a destination PQC
account, one tx, authorized by caller's ECDSA key.
- `migrateToPqcAccount(factory, falconPubKey, salt, expectedDestination, ...)`:
DERIVES the destination from the Falcon-512 key via the live
AerePQCAccountFactory.predictAddress, so the sweep is cryptographically bound
to the PQC account that Falcon key controls. Optional expectedDestination guard.
- `predictPqcAccount(...)` view for frontends.
- Authorization documented BOTH ways: pre-approval (default) OR EIP-7702
delegation (Aere supports 7702 per the EIP matrix; single-tx approve+migrate).
- No custody (caller is `from`, destination is `to`, one hop; native forwarded
in full same-call). No owner/admin/upgrade/withdraw/rescue/sweep. Only recipient
is the caller-specified destination.
- Fail-closed + atomic: zero destination reverts; length/zero-token/zero-amount
checks; strict native (StrayNative / ZeroNative / NothingToMigrate); any failed
transfer reverts the WHOLE tx (SafeERC20). Emits Migrated(old, new, tokens,
amounts, nativeAmount).
2. Test: `aerenew/contracts/test/account-migrator.test.js`. RAN it:
`npx hardhat test test/account-migrator.test.js` -> **9 passing** (2026-07-19).
Destination in strong-path tests is a REAL AerePQCAccount deployed via the REAL
AerePQCAccountFactory (Falcon verify mocked; CREATE2 + asset moves fully real).
Covers: one-tx ERC-20 migration (both entrypoints), native forward, zero-dest
revert, atomicity (first token NOT moved on a failed second leg), only-destination
(third party gets nothing), no-admin-drain (ABI has no owner/withdraw/rescue/
sweep; force-sent tokens unrecoverable), fail-closed native + malformed sets.
3. Doc: `aerenew/docs/AERE-QUANTUM-MIGRATION.md`. Dual:
- Part A (real): harvest-now-decrypt-later rationale, the live PQC destinations
(AerePQCAccount / AerePQCAccountFactory / AereHybridAuth / verifier / key
registry), the contract, user flow, and the real 9/9 test result. References
the pqc-migration-toolkit as the companion scanner/derivation/simulator.
- Part B (design only, gated): Foundation operator key rotation = operational
role transfer to a PQC/threshold account, founder-gated, NOT coupled to
consensus. Validator signing key rotation = coupled to the gated consensus-PQC
activation (seals are classical ECDSA QBFT today), founder + external-audit +
N>=9 + soak gated. Explicit: none of this makes consensus post-quantum by itself.
## Honesty flags carried in the doc
- `[VERIFY]`: live addresses (factory 0xd5315Ea7...CE58, sample account
0xa42a5e7F...4326f, verifier 0x4E8e9682...D8fFC, key registry 0x1eCa3c5A...3691);
AereHybridAuth is repo source, no mainnet address; which threshold account is the
intended operator destination.
- `[MEASURE]`: full operator role-rotation cost across the live role set;
consensus-PQC flip fork-freedom / halt-recover behavior at N>=9 soak.
## Scope boundary kept
Account migration hardens ACCOUNT authorization only. It does NOT make consensus
post-quantum. Validator-key rotation is coupled to the separate gated consensus-PQC
item, not a today-action. No new token; no real user/Foundation funds moved (this is
the mechanism, not an executed migration). No em-dashes in prose; brand "Aere
Network"/"Aere", ticker AERE.

306
AERE-QUANTUM-MIGRATION.md Normal file
View File

@ -0,0 +1,306 @@
# Aere Quantum Migration: Accounts and Network Keys
Status: user account migration is REAL and TESTED (contract + Hardhat suite,
below). Network key rotation is DESIGN ONLY and founder + audit gated. This
document is the last quantum-resistance layer at the key-management level (quantum
layer 11), and it is honest about which half is a user-callable contract today and
which half is a gated network operation.
Scope boundary, stated once and never crossed in this document: moving an account
(or a network operator) from a classical ECDSA key to a post-quantum key changes
who can authorize. It does NOT make Aere consensus post-quantum. Mainnet chain 2800
still seals blocks with classical secp256k1 QBFT, and the on-chain ZK verifiers
(BN254 Groth16) are classical. Validator-key rotation to a post-quantum scheme is
therefore coupled to the separate, gated consensus-PQC activation, not implied by
anything here.
Two flag conventions are used throughout:
- `[VERIFY]` a claim that should be confirmed against live chain state or a
deployment record before it is relied on.
- `[MEASURE]` a number or outcome that depends on a measurement or an operational
run that has not been performed here.
---
## Part A. User account migration (real, tested)
### A.1 Why migrate at all: harvest-now-decrypt-later
An Aere externally-owned account (EOA) is authorized by a secp256k1 ECDSA key. Two
facts make that a long-term liability once a cryptographically relevant quantum
computer exists:
1. ECDSA over secp256k1 is broken by Shor's algorithm. A sufficiently large
quantum computer recovers the private key from the public key.
2. An EOA reveals its public key the first time it sends a transaction (the public
key is recovered from the signature). So the exposure is not hypothetical for
any account that has ever transacted; the key material an attacker needs is
already on chain.
The threat is not "funds move today." It is "an adversary records the chain now and
breaks the keys later." That is the harvest-now-decrypt-later threat applied to
account authorization: long-dormant, high-value holdings sitting under a classical
key are exactly the accounts most worth attacking on a horizon of years. The
mitigation is to move such holdings, while the classical key is still safe, onto an
account whose spending authority is a post-quantum key.
Aere already ships the destinations for that move as live primitives. What was
missing, and what this document delivers, is a clean, no-custody path to perform
the move. That gap is quantum layer 11 in `aerenew/build-tracker/control.json`.
### A.2 The destinations: Aere's live post-quantum account primitives
The migration does not invent a new account type. It targets the ones Aere already
runs:
| Primitive | What it is | Auth | Status |
|---|---|---|---|
| `AerePQCAccount` | ERC-4337 v0.7 smart account owned solely by a NIST Falcon-512 key (897-byte pubkey) | Falcon-512 only, no ECDSA fallback | live sample `0xa42a5e7F...4326f` `[VERIFY]` |
| `AerePQCAccountFactory` | CREATE2 factory; account address is a pure function of (falconPubKey, salt) | n/a (permissionless deploy) | live `0xd5315Ea7...CE58` `[VERIFY]` |
| `AereHybridAuth` | Defense-in-depth: authorizes only if BOTH an ECDSA leg and a Falcon-512 leg verify over the same 32-byte hash | ECDSA AND Falcon-512 | repo source, no mainnet address `[VERIFY]` |
| `AereFalcon512Verifier` | Live Solidity Falcon-512 verifier the accounts delegate to | n/a | live `0x4E8e9682...D8fFC` `[VERIFY]` |
| `AerePQCKeyRegistry` | Proof-of-possession key registry | n/a | live `0x1eCa3c5A...3691` `[VERIFY]` |
Two destination shapes matter for a holder:
- PQC account (Falcon-512 only). Spending authority becomes a single lattice key.
Strongest post-quantum posture, but there is no ECDSA fallback: lose the Falcon
key and the account is gone. Register the key in `AerePQCKeyRegistry` and set PQC
social-recovery guardians BEFORE moving material value.
- Hybrid account (ECDSA + Falcon-512, via `AereHybridAuth`). Transition-era: an
attacker must break BOTH secp256k1 and Falcon-512. Good for a staged move while a
holder still wants a classical leg. `AereHybridAuth` is repo source with no
mainnet address as of 2026-07-19, so standing it up is a founder/deployer step
`[VERIFY]`.
The counterfactual address of a Falcon key's PQC account is pure CREATE2 math and
can be derived off-chain (see the migration toolkit, `aerenew/pqc-migration-toolkit`,
`predictPqcAccountAddress`, verified there against the live sample account). The
account can receive assets at that address before it is deployed, and be deployed
later via `createAccount`, which is permissionless and idempotent.
### A.3 The contract: `AereAccountMigrator`
File: `aerenew/contracts/contracts/pqc/AereAccountMigrator.sol` (solc 0.8.23,
OpenZeppelin SafeERC20 + ReentrancyGuard).
It is a pure conduit that atomically moves a caller-specified set of ERC-20
balances, and optionally native AERE, from the caller (the old EOA) to a
destination post-quantum account, in one transaction authorized by the caller's
ECDSA key. Two entrypoints:
- `migrate(destination, tokens[], amounts[], moveNative)`: move to an explicit
destination address.
- `migrateToPqcAccount(factory, falconPubKey, salt, expectedDestination, tokens[], amounts[], moveNative)`:
DERIVE the destination from a Falcon-512 key via the live
`AerePQCAccountFactory.predictAddress`, so the holder gets cryptographic
assurance they are sweeping into the post-quantum account their Falcon key
controls, and nowhere else. `expectedDestination` is an optional guard: if
non-zero it must equal the derived address, else the call reverts.
A read-only `predictPqcAccount(factory, falconPubKey, salt)` lets a frontend
confirm the destination before signing.
#### Authorization: pre-approval, or EIP-7702
The move is authorized by the caller's classical ECDSA key. The migrator does not
invent an authorization scheme; it uses ordinary ERC-20 allowances. Two supply
patterns, both documented:
1. Pre-approval (default, works today, no protocol feature). The holder sends one
`approve(migrator, amount)` per ERC-20, then calls `migrate`. Each
`safeTransferFrom` consumes the allowance and moves the tokens straight to the
destination.
2. EIP-7702 delegation (single-transaction UX). Aere supports EIP-7702 (see
`aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md`, row 7702 "Supported", the
EOA-delegation primitive Aere carries in place of EIP-3074). The EOA can, in one
transaction, temporarily set its account code to a batching delegate that both
approves and calls `migrate`, removing the separate approve transactions. The
migrator is agnostic to which pattern is used; under EIP-7702 the caller is
still the EOA and every invariant below is unchanged.
#### No custody, no admin (the core safety property)
- In `migrate` the caller is the `from` of every `SafeERC20.safeTransferFrom`, and
the caller-specified `destination` is the `to`. Tokens move caller to destination
in one hop. The migrator is never the recipient, so it never holds an ERC-20
balance.
- Native AERE (`msg.value`) is forwarded, in full, to that same `destination`,
inside the same call, guarded by `nonReentrant`.
- There is no owner, no admin, no upgrade, no initializer. There is no withdraw,
rescue, or sweep function, so anything accidentally sent to the migrator can
never be pulled out by anyone (stuck-but-safe, not drainable).
- There is no code path that sends value to any address other than the
caller-specified destination.
#### Fail-closed and atomic
- `destination` must be non-zero (`ZeroDestination`).
- Token and amount arrays must be equal length (`LengthMismatch`); each token
non-zero (`ZeroToken`); each amount non-zero (`ZeroAmount`).
- Native is strict: `moveNative` true requires non-zero `msg.value` (`ZeroNative`);
any `msg.value` sent while `moveNative` is false reverts (`StrayNative`), so value
can never be stranded. An empty request (no tokens, no native) reverts
(`NothingToMigrate`).
- A single failing transfer reverts the WHOLE transaction (SafeERC20 reverts on a
false/failing ERC-20), so the migration is all-or-nothing. It can never leave a
holder with a partially drained old account.
#### The `Migrated` event
`Migrated(address indexed oldAccount, address indexed newAccount, address[] tokens, uint256[] amounts, uint256 nativeAmount)`
binds the old classical account, the new post-quantum account, and the exact asset
set that moved. This is the on-chain, auditable record of a migration.
### A.4 The user flow
1. Choose a destination. Generate (or reuse) a Falcon-512 key; derive its
counterfactual `AerePQCAccount` address off-chain (toolkit
`predictPqcAccountAddress`, or the on-chain view `predictPqcAccount`). For a
hybrid transition, register an `AereHybridAuth` identity instead.
2. Prepare recovery FIRST. If the destination is Falcon-only, register the key in
`AerePQCKeyRegistry` (proof-of-possession) and set PQC social-recovery
guardians before moving material value. A Falcon-only account has no ECDSA
fallback.
3. Approve. From the old EOA, `approve(migrator, amount)` per ERC-20 (or bundle the
approvals with the migrate via EIP-7702).
4. Migrate. Call `migrateToPqcAccount(...)` (recommended, destination is bound to
your Falcon key) or `migrate(destination, ...)`. One transaction moves the
whole asset set plus optional native AERE to the destination. The `Migrated`
event is your receipt.
5. Deploy the account when convenient. `AerePQCAccountFactory.createAccount` is
permissionless and idempotent; the account can be deployed before or after the
assets arrive at its counterfactual address.
The migration toolkit (`aerenew/pqc-migration-toolkit`) is the companion that
scans an account's quantum exposure (RED/YELLOW/GREEN), derives the destination
address, and prints a step-by-step plan and a gas estimate. This contract is the
on-chain executor for step 4.
### A.5 Test result (real)
Suite: `aerenew/contracts/test/account-migrator.test.js`. Run from
`aerenew/contracts` with `npx hardhat test test/account-migrator.test.js`.
Result on 2026-07-19: 9 passing.
The destination in the strong-path tests is a REAL `AerePQCAccount` deployed
through the REAL `AerePQCAccountFactory` (the Falcon verifier is mocked because a
genuine Falcon-512 signature cannot be produced inside the hardhat process; the
CREATE2 derivation and every asset movement are fully real). Covered:
- a user migrates ERC-20 balances from an EOA to a destination in one tx (both
`migrate` and the Falcon-derived `migrateToPqcAccount`);
- native AERE forwards to the destination, with nothing stuck in the migrator;
- a zero destination reverts (`ZeroDestination`);
- a failed token transfer reverts the whole migration, and the first (would-be
successful) token is NOT moved (atomicity);
- the migrator sends only to the specified destination; an unrelated third party
receives nothing;
- no admin function exists (no owner/withdraw/rescue/sweep/transferOwnership/
initialize in the ABI), and tokens force-sent to the migrator cannot be pulled
out by anyone;
- fail-closed native handling (`StrayNative`, `ZeroNative`, `NothingToMigrate`) and
malformed asset sets (`LengthMismatch`, `ZeroToken`, `ZeroAmount`).
---
## Part B. Network key rotation (design only, founder + audit gated)
This half is DESIGN. Nothing here is a today-action, and nothing here is executed
by any contract in this repository. It is documented so the end-to-end
quantum-resistance picture is complete and honest about what remains.
There are two network-level classical keys, and they are NOT the same kind of
problem.
### B.1 Foundation operator key rotation (operational key management) `[MEASURE]`
What it is. The Foundation operator key is the ECDSA key that holds privileged
roles on operational contracts (for example NavOracle attestation, compliance
registries, the deployer/owner roles that have not yet been handed to the
Timelock). It is a normal account key, not a consensus key.
Why rotate it. Same harvest-now-decrypt-later logic as a user account: a long-lived
operator key under ECDSA is a standing liability on a multi-year horizon.
How, honestly. This is an operational key-management step, not a protocol change:
1. Stand up the post-quantum destination for the operator authority. The natural
choice is an `AerePQCAccount` (Falcon-512) or, better for an operator, a
post-quantum threshold account (the `AereThresholdAccount` family, PQC t-of-n
4337) so operator authority is not a single key. `[VERIFY]` which threshold
account is the intended operator destination.
2. Transfer each privileged role from the old ECDSA operator to the new
post-quantum account. On contracts that expose role transfer (owner /
role-grant), this is a sequence of ordinary role-transfer transactions. On
immutable core-economic contracts (Sink, sAERE, burn) there is no admin to
rotate, by design, so they need nothing.
3. Where governance is the intended long-term authority, the path is role to
Timelock, and the Post-Quantum Governance design
(`aerenew/docs/AERE-POST-QUANTUM-GOVERNANCE.md`) covers granting a PQC threshold
account the proposer/executor role on the Timelock.
Gating. This is founder-gated because it moves real privileged authority over live
contracts, and it is signed by the Foundation key, not by any contract here. It is
NOT coupled to consensus. The cost/outcome of a full rotation across the live role
set is `[MEASURE]` (it depends on the exact contracts and roles enumerated at
rotation time). No role is rotated by publishing this document.
### B.2 Validator signing key rotation (coupled to consensus-PQC) `[MEASURE]`
What it is. Each validator seals QBFT blocks with a classical secp256k1 key. This
is the consensus signing key, not an application account key.
The honest coupling. Today validator seals are classical ECDSA QBFT. Rotating a
validator's signing key to a post-quantum scheme is NOT an isolated key swap: the
consensus protocol itself has to accept and verify post-quantum seals. In other
words, a validator cannot "rotate to a Falcon seal" while the rest of the network
still expects an ECDSA seal. Validator-key rotation to PQ is therefore coupled to
the consensus-PQC activation, which is tracked as its own gated item:
- The in-place PQC activation work (hybrid ECDSA + Falcon BLOCKING seal, activated
via an anchor contract on the live chain, proven at small N in isolation) is the
mechanism that would let the seal set become post-quantum without a re-genesis.
- That flip is HIGH risk (it can halt the chain), and its gates are: N greater than
or equal to 9 validators, a soak period, an external consensus audit, and the
founder's explicit GO. See the mainnet queue in
`aerenew/build-tracker/control.json` ("Consens post-quantum LIVE", risk MARE).
So the sequence is: consensus-PQC activation FIRST (gated), and validator signing
keys rotate to their post-quantum form AS PART OF that activation, not before it
and not independently. Doing validator-key rotation "early" has no meaning while
consensus verifies classical seals.
Gating. Founder-gated AND external-audit-gated AND dependent on the consensus-PQC
item. Not a today-action. Nothing in this repository activates it. The outcome
(fork-freedom, halt/recover behavior at the target N) is `[MEASURE]` against a real
soak at N greater than or equal to 9.
### B.3 Summary of the network half
| Network key | Kind of change | Coupled to consensus? | Gate | Today-action? |
|---|---|---|---|---|
| Foundation operator key | Operational role transfer to a PQC account | No | Founder | No (founder-signed op) |
| Validator signing keys | Consensus seal scheme change | Yes | Founder + external audit + N>=9 + soak | No |
The single most important honesty point: none of this makes consensus post-quantum
by itself. The user account migration in Part A is a real, deployable-today,
no-custody contract that hardens ACCOUNT authorization. The network half in Part B
is the operator-key rotation (an operational step) plus the validator-key rotation
(inseparable from the gated consensus-PQC flip). This is the last quantum-resistance
layer, and it is largely gated, not a switch to flip tonight.
---
## Files
- `aerenew/contracts/contracts/pqc/AereAccountMigrator.sol` (contract)
- `aerenew/contracts/test/account-migrator.test.js` (Hardhat suite, 9 passing)
- `aerenew/pqc-migration-toolkit/` (scanner + derivation + cost simulator)
- `aerenew/contracts/contracts/pqc/AerePQCAccount.sol`,
`AerePQCAccountFactory.sol`, `AereHybridAuth.sol` (destinations)
- `aerenew/docs/AERE-POST-QUANTUM-GOVERNANCE.md` (Timelock role grant to a PQC
threshold account, the governance path for operator authority)
- `aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md` (EIP-7702 support)

View File

@ -0,0 +1,464 @@
# Aere Network Quantum-Safe Networking (Transport Layer)
**Spec #30, Wave 1. Design document. Status: NOT LIVE.**
> This document specifies making Aere's network **transport** post-quantum. It is a
> design and rollout spec, not a change log. Nothing described here is live on Aere
> mainnet at time of writing. The two parts have very different costs, and this
> document is explicit about that: the RPC part is close to a configuration and build
> change, the node-to-node part is real protocol engineering. Both are marked with
> their honest status inline.
Scope in one line: this is **transport confidentiality** work (protection against
harvest-now-decrypt-later on the wire). It does **not** touch consensus, and it is
**not** end-to-end application messaging. See Section 6 for the hard scope boundaries.
---
## 1. The gap: layer 12
Aere's quantum-resistance story is organized as a stack of layers. Most of that stack
is already covered or on a defined roadmap. Reading the current layer map
(`aerenew/build-tracker/control.json`, `quantumLayers`):
| Layer | What it is | State today |
| --- | --- | --- |
| 1. Signatures / accounts | PQC signature verification, PQC accounts | **have** (live on mainnet since the AerePQC fork, block 9,189,161) |
| 4. Encryption (ML-KEM) | ML-KEM precompile | **have** (precompile present; mainnet activation gated) |
| 3. Verification (zk proofs) | PQ STARK verify, off BN254 | building (#2), scope-corrected (see note) |
| 2. Consensus (validator signatures) | PQC validator seals | next (built and tested, founder + audit + N>=9 gated) |
| 5..10 | finality/light-client, bridges, agent mandates, threshold, compliance, oracles | next (roadmap) |
| 11. Migration (old accounts + network keys) | move legacy ECDSA funds to PQ, rotate network keys | **gap** (#14) |
| **12. Transport / networking** | **how nodes and clients actually connect** | **gap (this document, #30)** |
> Note on layer 3 (scope caveat, 2026-07-19 finding): the PQ STARK verifier skeleton (#2, 0x0AE8) built
> so far is a BabyBear + FRI STARK verifier, conformance-confirmed against Plonky3 and targeting Aere's
> OWN circuits. It does NOT yet verify SP1 6.1.0 proofs (SP1 6.1.0 is Hypercube / KoalaBear multilinear,
> a different stack), so "off BN254" for the SP1 zk-verifier path is a separate ~22 to 32 person-week
> retarget, not imminent. On-chain zk verifiers remain classical BN254 today. See
> `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Layers 1 through 11 are about *what the chain signs and verifies*. Layer 12 is about
*the pipes*: the encrypted channels over which a wallet talks to an RPC endpoint, and
over which one node talks to another. Today those pipes negotiate their session keys
with classical elliptic-curve cryptography (X25519, secp256k1). Both key exchanges are
broken by a sufficiently large quantum computer running Shor's algorithm. That is the
gap this document closes on paper and stages for rollout.
Important framing, stated up front so it is not lost: the symmetric ciphers on these
channels (AES-256-GCM on TLS, AES-256 on the node-to-node transport) are **not** the
problem. Grover's algorithm only halves their effective strength, which leaves AES-256
at a 128-bit security floor, still sound. The Shor-vulnerable part of both channels is
exclusively the **key agreement** (how the two sides derive a shared secret). So the
whole of layer 12 reduces to one move, applied in two places: replace or augment the
classical key agreement with a hybrid post-quantum key-encapsulation mechanism (KEM).
---
## 2. Threat model: harvest-now-decrypt-later
The concrete threat this work addresses is **harvest-now-decrypt-later (HNDL)**: an
adversary records encrypted traffic today, stores it, and decrypts it years later once
a cryptographically relevant quantum computer exists. Anything whose confidentiality
must outlive the arrival of that machine is at risk the moment it crosses a
classically-keyed channel.
Two properties of this threat shape the whole design:
1. **It is a passive, present-day attack.** The adversary does not need a quantum
computer now. It needs a tap and a disk now, and the quantum computer later. This is
why the fix is worth doing before quantum computers exist, and why "we will upgrade
when we have to" is the wrong posture: traffic recorded today is already committed.
2. **It targets confidentiality, via the key exchange.** HNDL breaks the recorded
session by recovering the session key from the recorded handshake. It does not
require breaking authentication in real time. This is exactly why hybridizing the
**KEM** (not necessarily the certificate or the signature) is the effective and
sufficient first move, and why this is a transport-confidentiality document, not a
transport-authentication one. PQ authentication of the channel (PQ certificates, PQ
handshake signatures) defends a **different** threat, an active quantum MITM at
connection time, and is a later, separate concern noted in Section 6.
What is genuinely sensitive on each Aere transport surface, and therefore worth HNDL
protection, differs between the two surfaces, and the document is honest about that in
each part (Section 4.6 for RPC, Section 5.4 for devp2p).
---
## 3. Where transport crypto lives in Aere today
There are exactly two transport surfaces on the network, and they use **different**
cryptographic machinery. This is the single most important fact for understanding why
the effort splits so unevenly.
**Surface A: public JSON-RPC over TLS.** A wallet, dapp, indexer, or `ethers.js`
client connects to `rpc.aere.network` or `rpc2.aere.network` over HTTPS. TLS is
terminated at **nginx** on the box, which reverse-proxies cleartext to the local Besu
node on `127.0.0.1:8545` (Besu's RPC port is not publicly bound). This is confirmed by
the live setup for the second public RPC (`aerenew/docs/RPC2-NODE-2026-07-17.md`,
Section 3 and Section 5): nginx owns ports 80/443, terminates Let's Encrypt TLS, and is
the sole public path to the node. The same shape holds for the primary infra RPC. The
key exchange here is **ordinary TLS 1.3**, today negotiating classical groups (X25519
and the NIST P-curves).
**Surface B: node-to-node over devp2p / RLPx.** Validators and full nodes connect to
each other over Ethereum's devp2p protocol on TCP/UDP port 30303 (the `rpc2` firewall
opens exactly this: `30303 tcp` for RLPx and `30303 udp` for discovery). The encrypted
transport under devp2p is **RLPx**, and RLPx does **not** use TLS. It has its own
handshake: an ECIES scheme (Elliptic Curve Integrated Encryption Scheme) over the
**secp256k1** curve, deriving AES and MAC keys for the framed session. This is a
completely separate piece of cryptography from TLS, living inside the Ethereum client's
networking stack.
The consequence: making Surface A post-quantum is a change to a well-known, widely
supported, standards-track TLS knob at a standard TLS terminator (nginx + OpenSSL).
Making Surface B post-quantum is a change to a bespoke handshake implemented in the
guts of Besu and Nethermind, with no standards-track PQ variant to simply switch on.
That asymmetry is the whole story of this document.
---
## 4. Part A: post-quantum TLS on the public RPC (the near-config part)
**Effort: low. This is largely a build and configuration change, not a protocol change.
Status: not live; straightforward enablement.**
### 4.1 The hybrid group: X25519MLKEM768
The mechanism is a **hybrid** TLS 1.3 key-exchange group, `X25519MLKEM768`. "Hybrid"
means the handshake runs **two** key exchanges concurrently and combines their outputs
into the session secret:
- **X25519**, the classical elliptic-curve Diffie-Hellman already in use, and
- **ML-KEM-768** (the NIST FIPS 203 standardized module-lattice KEM, formerly Kyber).
Because the shared secret mixes both, the session is secure as long as **either**
primitive holds. A quantum adversary who breaks X25519 still faces ML-KEM-768; a
lattice cryptanalysis result that dents ML-KEM still leaves X25519. Hybrid is therefore
**strictly no weaker** than the classical group it replaces, which is exactly why it is
safe to deploy now rather than betting the channel on a young post-quantum primitive
alone.
Real-world status of this specific group, which is what makes Part A cheap:
- It is the **IETF standards-track** hybrid (the `X25519MLKEM768` named group, IANA TLS
Supported Groups code point `0x11EC`), the successor to the earlier experimental
`X25519Kyber768Draft00`.
- **OpenSSL 3.5** (released April 2025, an LTS release) supports `X25519MLKEM768`
**natively**, with no external provider required. It also ships the related
`SecP256r1MLKEM768` and `X448MLKEM1024` groups and native ML-KEM / ML-DSA / SLH-DSA.
- **Chrome / BoringSSL** ship `X25519MLKEM768` and negotiate it by default, so a large
fraction of real clients already offer the group. Firefox has shipped it as well.
- For OpenSSL 3.x builds **older** than 3.5, the reference way to add the group is
**oqs-provider** loaded through the OpenSSL provider interface, backed by the
**liboqs** library (the Open Quantum Safe project). This is the fallback path if a box
cannot yet run OpenSSL 3.5.
### 4.2 Build prerequisite (the one real cost of Part A)
The only non-trivial step is that the TLS terminator must be linked against a crypto
library that knows the group. Concretely, one of:
- **Path 1 (preferred): OpenSSL 3.5+.** Build or install nginx linked against OpenSSL
3.5 or newer. The group is then built in. This is the clean long-term path.
- **Path 2 (bridge): OpenSSL 3.x + oqs-provider.** Keep the existing OpenSSL, add the
`oqsprovider` module via `openssl.cnf`, which registers `X25519MLKEM768` as an
available group. This avoids rebuilding the base TLS stack but adds an out-of-tree
provider dependency.
The Aere RPC boxes run Ubuntu 24.04, whose default OpenSSL is in the 3.0.x series and
therefore does **not** know `X25519MLKEM768` out of the box. So Part A is honestly "a
build/packaging step, then configuration," not "one line and reload." The exact OpenSSL
version currently linked into nginx on the infra box and on `rpc2` is **[VERIFY]** (run
`nginx -V` and `openssl version` on each), which decides Path 1 vs Path 2.
Once the terminator understands the group, everything after is configuration.
### 4.3 nginx configuration
nginx exposes the TLS 1.3 key-exchange group list through the `ssl_ecdh_curve`
directive (which maps to OpenSSL's group-list API). The change is to prepend the hybrid
group ahead of the classical ones, so it is preferred when the client offers it and the
classical groups remain as fallback:
```nginx
server {
listen 443 ssl;
http2 on;
server_name rpc.aere.network;
# Post-quantum hybrid first, classical groups retained as fallback.
ssl_ecdh_curve X25519MLKEM768:X25519:secp256r1;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_certificate /etc/letsencrypt/live/rpc.aere.network/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rpc.aere.network/privkey.pem;
# ... existing reverse-proxy to 127.0.0.1:8545, CORS handling,
# rate limiting, HSTS, host allowlist as already deployed ...
}
```
Notes that keep this honest and safe:
- The hybrid group applies to **TLS 1.3** key exchange. TLS 1.2 remains classical; that
is acceptable because modern RPC clients negotiate TLS 1.3, and TLS 1.2 stays only as
a compatibility floor.
- Ordering the list with the hybrid **first** means a capable client (Chrome/BoringSSL,
OpenSSL 3.5 clients, recent `ethers`/`viem` runtimes on a modern Node/OpenSSL) gets
the PQ group, and an old client that does not offer it falls back to X25519 with no
handshake failure. There is no flag day for clients.
- The server **certificate** is unchanged and still classical (the existing Let's
Encrypt RSA/ECDSA cert). This is deliberate and correct for the HNDL threat (Section
2): the KEM protects session confidentiality; PQ certificate authentication is a
separate, later concern (Section 6).
- Whether the specific nginx build in production honors `X25519MLKEM768` via
`ssl_ecdh_curve` (versus needing a newer nginx that passes the group list straight
through to OpenSSL 3.5) is **[VERIFY]** against the actual `nginx -V` on the boxes.
### 4.4 How a client verifies the negotiated group
The point of the change is only real if a client can confirm the PQ group was actually
used. With OpenSSL 3.5:
```bash
openssl s_client -connect rpc.aere.network:443 -groups X25519MLKEM768 </dev/null 2>/dev/null \
| grep -i "Negotiated TLS1.3 group"
# expected: Negotiated TLS1.3 group: X25519MLKEM768
```
Other independent checks:
- **Chrome DevTools**, Security panel, shows the key-exchange group for the connection.
- A negative control: `openssl s_client ... -groups X25519` should still connect
(classical fallback works), proving the change is additive and not breaking.
A published verification snippet like the above belongs on the "verify yourself" page
(`quantum.html`) so that the claim "Aere RPC negotiates post-quantum TLS" is
independently checkable rather than asserted.
### 4.5 Backward compatibility and failure modes
- **Old clients:** unaffected. They never offer the hybrid group and negotiate X25519 as
today.
- **New clients:** get the hybrid group automatically.
- **Handshake size:** ML-KEM-768 key shares are larger than an X25519 share (roughly a
kilobyte-scale ClientHello key share versus 32 bytes). This is well within TLS limits
and is the same overhead Chrome already ships to the whole web; it is a non-issue for
RPC. Worth a one-time latency spot check after enablement, marked **[MEASURE]**.
- **Rollback:** revert one directive (`ssl_ecdh_curve`) and reload nginx. There is no
chain state involved, no consensus impact, and no persistent change. This is as low a
blast radius as an infra change gets.
### 4.6 What Part A does and does not protect
- **Protects:** the confidentiality of RPC session traffic against HNDL. This is the
genuinely valuable case, because RPC sessions can carry things whose confidentiality
matters, for example the linkage between a client IP and the set of addresses it
queries (`eth_getBalance`, `eth_call` targets), pending transactions submitted via
`eth_sendRawTransaction` before they are public, and query patterns that deanonymize a
user. Recorded today, those are exactly the sessions an HNDL adversary would want to
decrypt later.
- **Does not protect:** on-chain data itself (it is public by design), nor the integrity
or authenticity of the RPC response (that is TLS authentication plus, for the truly
careful, client-side verification against block hashes). It also does not defend an
active quantum MITM at connection time (PQ authentication, out of scope, Section 6).
### 4.7 Status
Not live. This is a straightforward enablement gated only on the build prerequisite
(Section 4.2) and a founder go for touching the public RPC terminators. It carries no
consensus risk and is fully reversible. It is the right first move (Section 7).
---
## 5. Part B: post-quantum devp2p / RLPx between nodes (the real-engineering part)
**Effort: high. This is protocol engineering inside the client networking stack, not
configuration. Status: roadmap. Implementation and interop are [MEASURE].**
### 5.1 What RLPx does today
Node-to-node links use RLPx, the encrypted transport beneath devp2p. Its handshake is
**not** TLS and shares none of TLS's tooling. In outline, as implemented in the Ethereum
clients Aere runs (Besu on the validators and infra, Nethermind as the second client):
1. The initiator sends an **auth** message; the recipient replies with an **ack**.
2. Both are encrypted with **ECIES over secp256k1** (the same curve Ethereum uses for
account keys). Each side contributes an ephemeral secp256k1 key; an ECDH between the
ephemeral keys, mixed with the static node keys and nonces, derives the session
secrets.
3. From that shared secret the session derives **AES-256** frame-encryption keys and MAC
keys, and all subsequent devp2p frames (block and transaction gossip, discovery,
sub-protocol messages) ride that symmetric session.
As with TLS, the symmetric layer (AES-256) is quantum-fine. The Shor-vulnerable part is
the **secp256k1 ECDH key agreement** inside the ECIES handshake. That is the single
thing that has to change.
### 5.2 What making it post-quantum requires
The move is conceptually the same as Part A, hybridize the key agreement, but there is
no switch to flip. It requires **patching the client's RLPx handshake** to run a hybrid
KEM (for example X25519 or secp256k1 ephemeral ECDH **plus** ML-KEM-768) and to feed the
combined secret into the existing key-derivation step. Concretely that means, in each
client:
- Extending the auth/ack handshake messages to carry an ML-KEM encapsulation alongside
the existing ephemeral public key, without breaking the RLP encoding that stock peers
expect.
- Mixing the ML-KEM shared secret into the KDF that currently derives the AES/MAC keys
from the ECDH secret alone.
- A **capability negotiation** so a hybrid-capable node and a stock node can still
connect: the patched node must detect whether the peer understands the extended
handshake and cleanly fall back to classical RLPx if not. Without this, turning on PQ
RLPx would partition the node from every peer that has not upgraded, which on a live
validator set is a liveness hazard, not just an inconvenience.
This lives in the networking internals of two independent codebases, Besu (Java, the
`p2p.rlpx` handshake path) and Nethermind (.NET, its RLPx handshake service). The exact
class and method surfaces to modify in each are **[VERIFY]** against the pinned client
versions Aere builds, and are deliberately not asserted here to avoid inventing detail.
### 5.3 Why this is real engineering, not config
- **No standards-track PQ RLPx exists.** Unlike `X25519MLKEM768` for TLS, there is no
ratified "PQ RLPx" named suite shipping in the upstream clients. Whatever Aere builds
is either a bespoke extension or a proposal that has to be driven upstream (a natural
candidate for the open AIP/EIP process rather than a private, proprietary variant).
- **It is a two-client, coordinated change.** Because Aere deliberately runs client
diversity (Besu plus Nethermind), a hybrid RLPx must be implemented **compatibly in
both**, or the two clients stop peering, which would break the very diversity property
it is meant to strengthen. That is a cross-codebase protocol-compatibility problem,
the hardest kind.
- **It touches the live peer graph.** Rolling it across a running validator set has to be
done behind negotiation and fallback (Section 5.2) and soaked on a testnet first,
exactly the caution the fault-injection and cross-client-determinism work exists to
provide. This is roadmap-grade change management, not a config reload.
### 5.4 An honest note on how much this buys today
The confidentiality value of PQ RLPx is **smaller today than PQ RPC-TLS**, and this
document says so plainly. Most of what crosses devp2p on a public blockchain is
**public data**: blocks and transactions that are, by design, broadcast to everyone and
land on-chain in cleartext seconds later. Recording that traffic under HNDL yields
information the adversary can already read from the chain.
The parts of devp2p traffic that are genuinely confidential are narrower: the peer
topology and timing (who connects to whom, useful for eclipse/deanonymization work), and
crucially **any future private payload that rides the p2p layer**. That second category
is where the value grows sharply: if Aere ships a threshold-encrypted mempool (roadmap
item, anti-MEV), then pre-ordering transaction contents would flow encrypted over
devp2p, and HNDL protection on that channel becomes materially important. So the honest
sequencing is: PQ RLPx is the correct long-term completion of layer 12, but its urgency
is **lower than PQ RPC-TLS now** and **rises with encrypted-mempool / private-flow
features later**. It should be built deliberately, not rushed ahead of the RPC win.
### 5.5 Status
Roadmap. Design as above; implementation, cross-client interop, negotiation/fallback
behavior, and testnet soak results are all **[MEASURE]** (none performed yet). This is
real client-networking engineering and should be scoped and gated as such (founder go,
plus the same testnet-soak discipline used for other consensus-adjacent changes).
---
## 6. Scope boundaries (stated plainly)
These boundaries are load-bearing. Read them literally.
1. **Transport confidentiality only.** This entire document is about protecting the
confidentiality of data in transit against harvest-now-decrypt-later. It is not about
authentication, not about integrity beyond what the existing channels already
provide, and not about anything on-chain.
2. **Consensus is untouched and stays classical.** Aere consensus remains classical
ECDSA QBFT. Nothing in this document changes how blocks are proposed, sealed, or
finalized. PQ transport and PQ consensus are different layers (12 versus 2); the
consensus flip is a separate, higher-risk, founder-and-audit-gated item and is not in
scope here.
3. **The PQC signature/account layer is separate and already live.** Aere's live
post-quantum signature verification and PQC accounts (layer 1, the AerePQC fork) are a
different mechanism from transport key exchange. This document neither depends on nor
modifies them. A wallet can use a PQC account over classical TLS today, and will be
able to use an ECDSA account over PQ TLS after Part A; the two are orthogonal.
4. **Post-quantum secure messaging is UMBRA's domain, not Aere's transport work.**
End-to-end encrypted **application messaging** (a user messaging another user with
metadata privacy and PQ E2EE) is the province of **UMBRA**, a separate project with
its own PQXDH-based message path. It is explicitly **not** part of Aere's transport
layer and must not be conflated with it. Aere layer 12 secures the pipes between nodes
and between clients and RPC endpoints; UMBRA secures messages between people. They are
different systems solving different problems. Do not merge the narratives.
5. **PQ authentication of the channel is a later, separate concern.** Hybridizing the KEM
defends confidentiality (the HNDL threat). Post-quantum **authentication** of the
channel (PQ TLS certificates, PQ handshake signatures, PQ node identities on RLPx)
defends against an active quantum adversary impersonating an endpoint at connection
time. That is a distinct threat on a longer horizon and is out of scope for this
document, which is deliberately about confidentiality first.
6. **Nothing here is live.** RPC PQ-TLS is a straightforward enablement pending a build
step (Section 4.2). devp2p PQ is real engineering on the roadmap. Neither is deployed.
---
## 7. Rollout plan
Two phases, sequenced by the effort/value split this document has argued for.
**Phase 1: PQ TLS on the public RPC (do first).**
Low effort, immediate and real HNDL protection for the largest and most sensitive
transport surface (wallets and clients talking to `rpc.aere.network` and
`rpc2.aere.network`). Steps:
1. **[VERIFY]** the OpenSSL version linked into nginx on infra and on `rpc2` (`nginx -V`,
`openssl version`); choose Path 1 (OpenSSL 3.5+) or Path 2 (oqs-provider).
2. Stage the build on one endpoint first. `rpc2` is the natural canary: it is the newer,
independent, non-validator box, so a mistake there cannot affect validators and the
primary RPC stays up.
3. Add `ssl_ecdh_curve X25519MLKEM768:X25519:secp256r1;`, `nginx -t`, reload.
4. Confirm negotiation with the client check in Section 4.4; confirm classical fallback
still works (negative control).
5. **[MEASURE]** a one-time handshake-latency delta.
6. Publish the verification snippet on `quantum.html` so the claim is independently
checkable.
7. Roll the same change to the primary RPC. Rollback is one directive.
**Phase 2: PQ devp2p / RLPx (roadmap, after Phase 1).**
High effort, real cross-client protocol engineering, lower present-day urgency (Section
5.4). Steps, all gated and none started:
1. Design the hybrid-KEM RLPx handshake extension with explicit capability negotiation
and classical fallback; take it through the open AIP/EIP process rather than shipping
a proprietary variant.
2. Implement compatibly in **both** Besu and Nethermind; verify hybrid-capable and stock
peers interoperate.
3. Soak on an isolated testnet under the existing fault-injection and cross-client
determinism harnesses.
4. Roll across the validator set behind negotiation/fallback, founder-gated, with the
same change-management discipline as other consensus-adjacent work.
5. Reassess priority upward if/when a threshold-encrypted mempool or other private p2p
payload lands, at which point PQ RLPx stops being topology-only protection and starts
guarding genuinely confidential flow.
Completing both phases closes layer 12 of the quantum stack.
---
## 8. Open flags register
| Flag | Item |
| --- | --- |
| **[VERIFY]** | Exact OpenSSL version currently linked into nginx on the infra RPC box and on `rpc2` (decides OpenSSL-3.5 path vs oqs-provider path). |
| **[VERIFY]** | Whether the production nginx build honors `X25519MLKEM768` through `ssl_ecdh_curve`, or requires a newer nginx passing the group list to OpenSSL 3.5. |
| **[VERIFY]** | Exact RLPx handshake class/method surfaces to modify in Besu (Java `p2p.rlpx`) and Nethermind (.NET RLPx handshake) against the pinned client versions Aere builds. |
| **[MEASURE]** | One-time TLS handshake-latency delta on the RPC endpoints after enabling the hybrid group. |
| **[MEASURE]** | devp2p PQ: implementation, Besu/Nethermind interop, negotiation/fallback behavior, and testnet soak results (none performed yet). |
---
*Aere Network. Spec #30, Wave 1. Transport-confidentiality design. Consensus stays
classical ECDSA QBFT; PQ signatures/accounts are a separate, live layer; application
messaging is UMBRA's separate domain. Nothing in this document is live.*

495
AERE-REPRODUCIBLE-BUILDS.md Normal file
View File

@ -0,0 +1,495 @@
# spec-28: Aere Network Reproducible Builds and Deterministic Release
Prepared 2026-07-19. This is the rebuild-the-binary manifest for Aere Network. Its companion,
[spec-23 / REPRODUCE.md] (`aerenew/REPRODUCE.md`), is the run-the-tests manifest: that document lets
a third party re-run our test suites, formal models, cryptographic known-answer tests, and
benchmarks. This document covers the other half of supply-chain integrity: how a third party rebuilds
the node client and the contract bytecode from source and confirms, by SHA256, that what is running
matches what was published. The two are meant to be read together and deliberately do not duplicate
each other.
Honesty tags used throughout:
- **[REAL]** the artifact exists in the tree at the cited path and was inspected for this document.
- **[CONFIRMED]** additionally exercised here (a script was run, a command produced output) on
2026-07-19.
- **[VERIFY: ...]** a claim that could not be confirmed from the tree alone and must be checked
before it is trusted.
- **[MEASURE: ...]** a heavy build or a digest that was not computed here. Run the stated command to
produce the real value. Do not quote a figure until you have.
If you find a bare claim without a tag where one is warranted, treat it as a bug in this document.
---
## 1. Why reproducible builds matter
A blockchain that "nobody owns" is only as trustworthy as the operator's ability to check what code
is actually running. Reproducible builds turn that from a promise into a checkable property:
> Anyone with the source tree at a given commit can rebuild the byte-identical client image (and the
> byte-identical contract bytecode) that the network runs, and confirm the match by SHA256.
The supply-chain threat this closes is a tampered binary masquerading as the real release. If the
published release carries a canonical SHA256 digest, and a stranger can independently rebuild from
public source and land on the same digest, then a modified binary (a backdoored signer, a silent
consensus change, an exfiltration hook) cannot pass as the genuine artifact: its digest will differ,
and the difference is visible to everyone at once. This is the precondition for two claims the
project makes elsewhere:
1. "The Foundation cannot secretly modify the chain." Unenforceable unless the running code is
verifiable against published source.
2. "Permissionless validator entry without trusted setup." A community operator joining the
validator set (the live set is N=7 today, roadmap to 21) must be able to prove they are running
the same code as every other operator, without trusting the Foundation to tell them so.
Reproducibility is the mechanism. The rest of this document inventories what is built, states plainly
what is not yet live, and gives the exact commands.
---
## 2. What exists today (inventory)
Every path below is relative to the repository root `aerenew/` unless stated otherwise. All were read
for this document.
### 2.1 The reproducible node image build
**`aerenew/node/Dockerfile.reproducible`** [REAL]. A multi-stage Docker build that compiles
Hyperledger Besu at a pinned ref and assembles a distroless runtime image. The reproducibility levers
actually present in the file:
- **Base images pinned by digest, not tag.** The builder stage is
`FROM eclipse-temurin@sha256:...` and the runtime stage is
`FROM gcr.io/distroless/java21-debian12@sha256:...`. A tag can be retagged to point at different
bytes; a digest cannot. The file states the intent explicitly ("the tag is documentation; the
digest is the law").
- **`SOURCE_DATE_EPOCH` build arg** threaded into `ENV` and used to stamp fixed mtimes: every source
file is `touch -d "@${SOURCE_DATE_EPOCH}"` before the Gradle build and every install output is
re-touched after. Identical epoch means identical mtimes throughout, so archive contents do not
drift with wall-clock time.
- **Deterministic Gradle flags.** `./gradlew --no-daemon --no-build-cache --no-watch-fs
-Porg.gradle.parallel=false -PreproducibleBuild=true installDist`. No daemon state, no local build
cache, no filesystem watcher, single-threaded, reproducible-archive mode. `GRADLE_OPTS` also sets
`-Dorg.gradle.daemon=false -Dorg.gradle.parallel=false -Dfile.encoding=UTF-8`, and `LC_ALL=C`,
`TZ=UTC` fix locale and timezone.
- **Besu ref pinned** via `ARG BESU_REF=25.5.0`; the build clones
`https://github.com/hyperledger/besu` at `v${BESU_REF}` with `--depth=1` and then `rm -rf .git` so
no git metadata leaks into a layer.
- **Alpine toolchain packages pinned by version** in the builder: `git=2.45.1-r0 bash=5.2.21-r0
unzip=6.0-r14`, and there is no `apk upgrade` (upstream package mutation would break determinism).
- **Fixed UID/GID** (`USER 10001:10001`) so no `useradd -m` writes a fresh home-directory timestamp.
- **Runtime layer is distroless** (`gcr.io/distroless/java21-debian12`): no shell, no package
manager, no version drift in the shipped image. Only the deterministic Besu install, the AERE
`config/genesis.json`, and `config/config.toml` are copied in. Genesis and config are copied last
so an application-level config change does not invalidate the deterministic Besu layer above it.
- **OCI labels** record source, license (Apache-2.0), and `io.aere.reproducible="true"`.
**[VERIFY: the two `FROM ... @sha256:` digest values in the Dockerfile appear to be illustrative
placeholders, not registry-resolved digests.** The builder digest ends
`...a0c2e4f6a8c0e2f4a6c8e0a` and the distroless digest ends `...c1d0e9f8a7b6c5d4e3f2a1b0`; both are
visibly patterned rather than random SHA256 output. Before any real reproducible build can resolve,
these must be replaced with genuine digests, obtained with
`docker buildx imagetools inspect eclipse-temurin:21.0.3_9-jdk-alpine` and
`docker buildx imagetools inspect gcr.io/distroless/java21-debian12:latest`, and pinned. As written,
the structure is correct and the levers are real, but a `docker build` would fail to pull those exact
digests. This is the single most important gap in the "what exists" column.]
**Scope note.** This Dockerfile builds stock upstream Hyperledger Besu at `v25.5.0` plus the AERE
genesis and config. QBFT consensus and the 0.5 second block period are configured through
`config/genesis.json` and `config/config.toml`, not through a client source patch, so the mainnet
consensus binary in this image is upstream Besu with AERE configuration. The post-quantum precompile
work (Falcon, ML-DSA, SLH-DSA, ML-KEM, HashToPoint at `0x0AE1..0x0AE7`) is a **separate** build path
with its own pinned base commit; see section 2.5. [VERIFY: confirm whether the live N=7 validator
image is this stock-Besu-25.5.0 build or the PQC-precompile fork build, and if the latter, whether
`Dockerfile.reproducible` should target the fork ref rather than upstream `v25.5.0`. The two build
recipes exist in the tree independently and their relationship to the live binary should be stated
explicitly.]
### 2.2 The third-party verify script
**`aerenew/scripts/verify-besu-image.sh`** [CONFIRMED]. A bash script that verifies a locally built
image against a published manifest. `./scripts/verify-besu-image.sh --help` was run here and returned
exit 0 with the usage banner, so the script is present and executable. What it does:
1. Requires `docker jq curl git` on PATH.
2. Resolves the local image digest: `docker image inspect "$IMAGE" -f '{{.Id}}'` (default image tag
`aere-besu:reproducible`). If the image is not present it prints the build command and exits 2.
3. Fetches the manifest JSON, either from a `--manifest` local file or from the URL
`https://aere.network/.well-known/besu-image-digest` (overridable with `--url`).
4. Parses `imageDigest`, `sourceCommit`, `sourceDateEpoch`, `besuRef`, `publishedAt`, `chainId` from
the manifest. Asserts `chainId == 2800` (exits 3 otherwise).
5. Compares the local digest against `imageDigest`. On mismatch it prints both digests, lists the
likely causes (wrong `SOURCE_DATE_EPOCH`, buildkit/arch difference, local edits to
genesis/config, upstream toolchain drift), and exits 4.
6. As a sanity check, confirms the manifest's `sourceCommit` exists in the local checkout via
`git rev-parse`. On match it prints the VERIFIED banner.
The script is real and self-consistent. Its comparison step, however, has nothing live to compare
against yet; see section 5.
### 2.3 The design and status document
**`aerenew/node/REPRODUCIBLE.md`** [REAL]. The narrative that accompanies the Dockerfile: how the
build achieves determinism, how to verify a running node, the intended published-manifest flow, the
weekly drift-detection cron, and the strategic rationale (audit-contest scoping, MiCA Article 22
operational controls, validator decentralization). Critically, it already carries an honest status
banner dated 2026-07-16 stating that the manifest is not yet published and that the end-to-end
guarantee is therefore not yet third-party verifiable. This document does not soften that; it
restates it in section 5.
### 2.4 The CI workflow
**`aerenew/.github/workflows/reproducible-besu-build.yml`** [REAL]. A GitHub Actions workflow that
mechanizes the double-build determinism check. Triggers: push to `main` touching `node/**`,
`config/genesis.json`, `config/config.toml`, or the workflow itself; manual `workflow_dispatch` with
an optional `besu_ref` input; and a weekly `cron: '0 3 * * 0'` (Sunday 03:00 UTC) for drift
detection. Job steps:
- Checkout with `fetch-depth: 0` (full history is needed to derive `SOURCE_DATE_EPOCH` from the HEAD
commit timestamp).
- Compute `SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct HEAD)`.
- Resolve `BESU_REF` from the `workflow_dispatch` input, falling back to the `ARG BESU_REF` line in
the Dockerfile.
- **BUILD #1 (canonical)** with `--provenance=mode=max --sbom=true`, capture image ID.
- **BUILD #2 (independent)** after `docker buildx prune` and with `--no-cache`, capture image ID.
- **Compare digests.** On mismatch, emit `::error::`, `docker save` both images, and diff their tar
listings for debugging, then fail.
- Write `artifacts/besu-image-digest.json` (version, chainId 2800, imageDigest, besuRef,
sourceDateEpoch, sourceCommit, workflowRun, publishedAt). This is exactly the schema
`verify-besu-image.sh` parses.
- Upload that artifact with 365-day retention.
- **Sigstore keyless attestation** via `actions/attest-build-provenance@v1` over the image digest
(`permissions: id-token: write, attestations: write`).
- **Publish manifest to `/.well-known`**, gated on `github.ref == 'refs/heads/main'` and a
`WELLKNOWN_DEPLOY_TOKEN` secret. If the secret is unset, the step prints a warning and exits 0
without publishing.
[VERIFY: whether `WELLKNOWN_DEPLOY_TOKEN` is configured in the repository's Actions secrets. Its
absence is the concrete reason no manifest is served today: the workflow reaches the publish step and
skips it. Also [VERIFY] whether this workflow has ever executed, since the public mirror repositories
it would run in are themselves not yet published, per REPRODUCE.md section 2.]
### 2.5 The PQC fork build recipe and its build record
The post-quantum precompile client is a distinct artifact from the mainnet node image, built by a
separate, pinned recipe. It is the "fork" whose KATs REPRODUCE.md section 3c covers.
- **`aerenew/pqc-fork/setup-fork.sh`** [REAL] pins the upstream Besu base commit
`BASE=d2032017bb3b8cb215a97303980a1e4a643f7180`, fetches it shallow, overlays the AERE PQC EVM
layer (five existing precompiles at `0x0AE1..0x0AE5`), then adds the two newer precompiles
(`0x0AE6` ML-KEM-768, `0x0AE7` Falcon HashToPoint), committing each stage for a clean patch.
- **`aerenew/pqc-fork/build-dist.sh`** [REAL] runs the single full build
`./gradlew -q :besu:installDist -x test` and lists the resulting `bin/besu` and lib jars.
- **`aerenew/parallel/combined-fork-binary/BUILD_INFO.txt`** [REAL] is a committed build-and-validation
record with real SHA256 checksums for a related fork artifact, including:
- install tarball `sha256 = 9561c40d591a10c03518a88423ae3098f8ee3791dbddb5f73ce388cd426276fc`
- `besu-evm.jar sha256 = 1d2bf44a08f306042c17276764fe05f040fe58371fb8e169fb1804bee815eb2d`
- `besu-ethereum-core.jar sha256 = c3b7ebee48e34ec83454d6f3e9dd5baf3261d87cedee1cebe574e7696b7478d2`
- the untouched "gold" isolator binary `sha256 = 7c5c0743088e9fa765d54c745814601b3c7a27a892e396156f88092b34c80772`
and a JDK-21, `./gradlew installDist -x test` rebuild recipe from a one-file diff. This is the
closest thing in the tree to a published expected-digest for a client artifact, though it is a
BUILD_INFO text record for a specific offline validation, not a signed manifest served at a stable
URL.
These checksums are jar-level and tarball-level SHA256, not the Docker image digest that
`verify-besu-image.sh` compares. They demonstrate that the project already computes and records real
build hashes; they are not yet wired into the manifest the verify script fetches.
### 2.6 The on-chain binary-commitment registry
**`aerenew/contracts/contracts/validators/AereValidatorManifest.sol`** [REAL] (with
`aerenew/contracts/test/validator-manifest.test.js`). A no-admin Solidity contract where each
validator's own QBFT signer key declares the SHA256 `binaryDigest` of the Besu image it runs plus a
`manifestUri` (typically the published `.well-known/besu-image-digest`), an operator label, and a
region. Commitments are keyed by signer with a monotonic serial and full event history; there is no
Foundation override. The contract's own NatSpec is honest that it cannot verify a validator actually
runs the claimed binary: that is the job of third-party probing plus the reproducible build. It gives
one canonical on-chain place to attest so audits do not drift across mediums. [VERIFY: whether this
contract is deployed on chain 2800 and, if so, at what address; the file and its test exist in the
tree but deployment was not confirmed here.]
### 2.7 The publication checklist
**`aerenew/GITHUB_DEPLOYMENT_CHECKLIST.md`** [REAL]. The gate that every public publish passes.
Section 3 below turns it into the deterministic release checklist. It is a GitHub-publication
checklist, explicitly not a mainnet deployment procedure: chain 2800 is already live and nothing in
it deploys a contract or touches consensus.
---
## 3. The deterministic release process
A release is deterministic when it runs the same way every time and produces the same published
artifacts from the same source commit. The checklist below composes the publication gate
(`GITHUB_DEPLOYMENT_CHECKLIST.md`), the reproducible-build CI
(`reproducible-besu-build.yml`), and the verify step (`verify-besu-image.sh`) into one repeatable
sequence. Steps marked (roadmap) are not yet live; see section 5.
1. **Freeze the source at a commit.** All release artifacts are keyed to one commit SHA. Nothing is
built from a dirty working tree. The commit timestamp is the single source of
`SOURCE_DATE_EPOCH`.
2. **Secret scan (hard gate).** No private keys, no `.env` files with real values, no API tokens, no
internal host addresses anywhere in the diff. The DevRel publishing flow gates publication on a
secret-scan pass and it must not be bypassed. (`GITHUB_DEPLOYMENT_CHECKLIST.md`, and
REPRODUCE.md section 1.)
3. **Claim honesty check.** No fabricated or stale performance claims. Every TPS, latency,
validator-count, block-time, supply, or "audited" statement in copy must match the checkable state
of the chain or an existing dated doc under `docs/`. In particular: consensus is Besu QBFT with
classical secp256k1 ECDSA (not proof-of-stake, not post-quantum); block time is 0.5 seconds with
single-slot BFT finality; total supply is 2,800,000,000 AERE fixed since genesis-v2 (2026-05-07);
273,000 TPS is a synthetic design ceiling, not a measured mainnet number, and must be stated that
way if it appears. No forbidden marketing superlatives ("world-class," "excellence," "gold
standard," "trusted by"). No em-dashes in prose copy. Address book (`sdk-js/src/addresses.ts`)
current with honest status for any referenced contract.
4. **Green CI (hard gate).** Build, the full test suite, and the secret scan run in CI, on a pull
request, before merge. Merge to a protected branch happens through a reviewed pull request, not a
direct push.
5. **Deterministic build, twice, byte-equal.** The `reproducible-besu-build.yml` workflow builds the
node image on a clean runner, wipes the buildkit cache, rebuilds `--no-cache`, and asserts the two
image IDs are identical. A mismatch fails the release and dumps a tar-listing diff for triage. The
weekly cron runs the same check to catch upstream toolchain drift before an operator does.
6. **Compute and record the canonical digest.** CI writes `besu-image-digest.json` (chainId 2800,
imageDigest, besuRef, sourceDateEpoch, sourceCommit, workflowRun, publishedAt) and uploads it with
365-day retention.
7. **Sign the build (provenance).** Sigstore keyless attestation over the image digest via
`actions/attest-build-provenance@v1`, plus `--provenance=mode=max --sbom=true` on build #1.
8. **Publish the manifest at a stable URL (roadmap).** POST `besu-image-digest.json` to
`https://aere.network/.well-known/besu-image-digest`, gated on `main` and on the
`WELLKNOWN_DEPLOY_TOKEN` secret. This is the step that makes third-party verification possible and
is not yet live (section 5).
9. **Tag the release honestly.** If the publish includes a release tag, the tag description matches
what actually shipped, not aspirational figures. (`GITHUB_DEPLOYMENT_CHECKLIST.md`.)
10. **Post-publication smoke checks.** Confirm `https://rpc.aere.network` responds before linking it.
Spot-check cited contract addresses with `eth_getCode` on the live RPC (non-empty result confirms
real deployment). (`GITHUB_DEPLOYMENT_CHECKLIST.md`.)
11. **Third-party verification path (roadmap, once step 8 is live).** A stranger checks out the
release commit, builds locally, and runs `verify-besu-image.sh`, which fetches the published
manifest and asserts a digest match. Until step 8 serves a real manifest, this step can only be
run against a `--manifest` local file, which proves local double-build determinism but not
agreement with a Foundation-published canonical digest.
Optionally, operators record their running binary's digest on chain via `AereValidatorManifest`
(section 2.6) so the attestation is queryable, not just claimed.
---
## 4. Contract bytecode reproducibility
The node client is one supply-chain surface; the deployed contracts are the other. Reproducing
contract bytecode means: compile the source with the pinned compiler and settings, and confirm the
result matches what is deployed on chain 2800 via `eth_getCode`.
**Pinned compiler and settings** (`aerenew/contracts/hardhat.config.js`) [REAL]:
- Global compiler `solc 0.8.23`, `optimizer.enabled = true`, `optimizer.runs = 1` (runs=1 chosen for
contract-size headroom, documented in-file), `viaIR = true`. Default EVM version is shanghai for
0.8.23.
- One per-file override: `contracts/AereCancunCanary.sol` compiles with `solc 0.8.24`,
`optimizer.runs = 200`, `evmVersion = "cancun"`. Every other contract stays on 0.8.23 so its
bytecode is untouched by the canary.
The Foundry profile (`aerenew/contracts/foundry.toml`) [REAL] independently pins `solc = '0.8.23'`,
`optimizer_runs = 200`, OpenZeppelin remapped to the single installed copy (OpenZeppelin 4.9.6). Note
the two profiles differ in `optimizer_runs` (Hardhat 1, Foundry 200) and Foundry is scoped only to
`contracts/pqc` for symbolic analysis, so the **functional deploy artifacts come from the Hardhat
build**; use the Hardhat settings when reproducing deployed bytecode, not the Foundry profile.
**Reproducing deployed bytecode:**
```bash
cd aerenew/contracts
npm ci # pinned toolchain (hardhat, solc 0.8.23, OZ 4.9.x)
npx hardhat compile # deterministic given the pinned solc + settings above
# The deployed runtime bytecode for a given contract is in
# artifacts/contracts/<Name>.sol/<Name>.json -> .deployedBytecode
```
Then compare against what is live:
```bash
# Fetch on-chain runtime bytecode for a deployed address on chain 2800:
curl -s -X POST https://rpc.aere.network \
-H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["<CONTRACT_ADDRESS>","latest"]}'
# Compare the returned 0x… string against the compiled .deployedBytecode.
```
**Honest caveat on the metadata hash.** `hardhat.config.js` does not set
`settings.metadata.bytecodeHash`, so solc's default applies and the compiled bytecode ends with a
CBOR-encoded metadata section that embeds a hash of the contract metadata (source, settings,
compiler). Two compiles agree on that trailer only when the full metadata matches, so a byte-exact
`eth_getCode` comparison requires the identical source, identical `solc 0.8.23`, identical optimizer
and `viaIR` settings, and identical import paths. If any of those differ, the executable opcodes can
still match while the metadata trailer differs. For a strict match, either reproduce the exact
metadata or compile with `metadata.bytecodeHash = "none"` on both sides and compare the code minus
the trailer. [MEASURE: pick one deployed contract, compile it under the pinned settings, and publish
both the compiled `deployedBytecode` and the `eth_getCode` result so the match (or the metadata-only
difference) is on the record. No specific contract byte comparison was run for this document.]
REPRODUCE.md section 3a is the companion here: it covers running the contract **test** suite (137
Hardhat test files, spot-checked green on 2026-07-19) and the symbolic layer. This document covers
reproducing the **bytecode**; the two together cover both "does it behave as claimed" and "is the
deployed code the compiled code."
---
## 5. Honest status: done vs roadmap
**Done and real today (local reproducibility):**
- The digest-pinned, multi-stage `Dockerfile.reproducible` with `SOURCE_DATE_EPOCH`, deterministic
Gradle flags, distroless runtime, and fixed UID/GID. [REAL]
- The `verify-besu-image.sh` script, executable and self-consistent, with a `--manifest` local-file
path that works without any network dependency. [CONFIRMED runs]
- The `reproducible-besu-build.yml` CI workflow: double build, byte-equal compare, provenance and
SBOM, Sigstore attestation, artifact upload. [REAL, execution not confirmed here]
- The publication gate (`GITHUB_DEPLOYMENT_CHECKLIST.md`): secret scan, claim honesty, green CI. [REAL]
- Pinned contract compiler settings (`solc 0.8.23`, optimizer, `viaIR`) enabling deterministic
contract compilation. [REAL]
- Real, recorded SHA256 checksums for fork jar and tarball artifacts in
`combined-fork-binary/BUILD_INFO.txt`, and pinned fork base commits in `pqc-fork/`. [REAL]
- The `AereValidatorManifest` on-chain attestation contract. [REAL, deployment unconfirmed]
**Roadmap, not yet live (third-party verifiability):**
- **The public digest manifest is not published.** This is the load-bearing gap. Per the status note
in `REPRODUCIBLE.md` (dated 2026-07-16), a live check of
`https://aere.network/.well-known/besu-image-digest` returned `Content-Type: text/html`, the site
SPA fallback, not the JSON manifest. So the third-party comparison step in `verify-besu-image.sh`
has nothing canonical to diff against. The CI publish step exists but is gated on a
`WELLKNOWN_DEPLOY_TOKEN` secret and skips (with a warning) when the secret is unset. State it
plainly: the build recipe exists and is reproducible locally; **publishing the canonical expected
digests, so a stranger can diff against them, is the remaining step.** [VERIFY: re-check the
`.well-known` URL and whether the deploy token is configured before making any "published" claim.]
- **The Dockerfile base-image digests appear to be placeholders** (section 2.1), so even a local
`docker build` will not resolve until real registry digests are pinned. This must be fixed before
the first genuine reproducible build. [VERIFY]
- **Kernel and host environment are not pinned** for operators. `REPRODUCIBLE.md` notes CI pins
`ubuntu-22.04` but operators run their own systems; a recommended host kernel plus a reference OS
image (Yocto / NixOS) is Phase 2.
- **Genesis-snapshot distribution** for operators bootstrapping from state (rather than from genesis)
needs its own signed snapshot manifest; tracked separately.
- **Public source repositories that the site and docs reference do not all resolve yet** (REPRODUCE.md
section 2, PASUL 0: `git.aere.network/aere-network/aerenew` returned 404 on 2026-07-19). Reproducible
builds presume the source is actually published; until it is, "rebuild from source" over-promises.
Bottom line: everything needed to reproduce the build **locally** and to prove double-build
determinism is in the tree. The one thing standing between that and a stranger independently
confirming a running validator matches a Foundation-published binary is the publication of the signed
digest manifest at a stable URL, plus replacing the placeholder base-image digests. Neither is a
research problem; both are wiring.
---
## 6. Reproduction commands
Concrete values from the current checkout (`aerenew/`, HEAD `6c55540`, commit timestamp
`1781391508` = 2026-06-14T01:58:28+03:00). Substitute the release commit when reproducing a specific
release.
### 6.1 Rebuild the node image and check double-build determinism
```bash
# From a checkout of the release commit:
cd aerenew
SDE=$(git log -1 --pretty=%ct HEAD) # SOURCE_DATE_EPOCH from the commit, e.g. 1781391508
# Build once.
docker build \
--build-arg SOURCE_DATE_EPOCH=$SDE \
--build-arg BESU_REF=25.5.0 \
-f node/Dockerfile.reproducible \
-t aere-besu:reproducible \
.
# Read the resulting image digest.
docker image inspect aere-besu:reproducible -f '{{.Id}}'
```
[MEASURE: this is a full Besu compile in Docker (tens of minutes, several GB). It was NOT run for
this document, and it will not resolve until the placeholder base-image digests in
`node/Dockerfile.reproducible` are replaced with real registry digests (section 2.1). Publish the
resulting image ID once the build succeeds; that value is the canonical digest.]
```bash
# Double-build determinism, the check CI automates:
docker build --build-arg SOURCE_DATE_EPOCH=$SDE --build-arg BESU_REF=25.5.0 \
-f node/Dockerfile.reproducible -t aere-besu:build1 .
docker buildx prune -f
docker build --no-cache --build-arg SOURCE_DATE_EPOCH=$SDE --build-arg BESU_REF=25.5.0 \
-f node/Dockerfile.reproducible -t aere-besu:build2 .
diff <(docker image inspect aere-besu:build1 -f '{{.Id}}') \
<(docker image inspect aere-besu:build2 -f '{{.Id}}') && echo "REPRODUCIBLE"
```
[MEASURE: two full builds. Not run here. The identical-ID assertion is the reproducibility property.]
### 6.2 Verify against a manifest
```bash
cd aerenew
# Against the published manifest (roadmap; not serving JSON yet):
./scripts/verify-besu-image.sh --image aere-besu:reproducible
# Against a local manifest file (works today, proves local determinism, not
# agreement with a Foundation-published canonical digest):
./scripts/verify-besu-image.sh --image aere-besu:reproducible --manifest ./local-manifest.json
```
[CONFIRMED: `./scripts/verify-besu-image.sh --help` was run on 2026-07-19 and returned exit 0. The
digest-comparison path was not exercised because no image was built here and no manifest is served.]
### 6.3 Reproduce contract bytecode
```bash
cd aerenew/contracts
npm ci
npx hardhat compile
# Compare artifacts/contracts/<Name>.sol/<Name>.json .deployedBytecode
# against eth_getCode for the deployed address (see section 4 for the curl call).
```
[MEASURE: run a specific contract's compile-and-compare and publish both byte strings. Mind the
metadata-hash trailer caveat in section 4. Not run for this document.]
### 6.4 Rebuild the PQC precompile fork (separate artifact, Linux)
```bash
cd aerenew/pqc-fork
bash setup-fork.sh # clones besu@d2032017, overlays fork EVM, adds 0x0AE6/0x0AE7 precompiles
bash build-dist.sh # ./gradlew :besu:installDist -x test (one full build)
```
[MEASURE: heavy Linux-only build, targets `$BESU_HOME` on a scratch box. Not run here, and never on
the live infra box, which runs production services. Publish the resulting `bin/besu` and jar SHA256
values and compare against `combined-fork-binary/BUILD_INFO.txt`.]
---
## 7. Relationship to REPRODUCE.md (spec-23)
`aerenew/REPRODUCE.md` (spec-23) and this document (spec-28) are the two halves of "verify Aere
yourself" and are meant to be non-overlapping:
- **spec-23 (REPRODUCE.md): run the tests.** Re-run the Hardhat suite, the z3 SMT formal models, the
PQC KAT/ACVP harnesses, and the gas benchmarks, and get the same results we get. It answers "does
the code behave as claimed."
- **spec-28 (this doc): rebuild the binary.** Rebuild the node image and the contract bytecode from
source and confirm the SHA256 match against the published release. It answers "is the running code
the published code."
Both are gated by the same publication gate (secret scan plus green CI) and both are honest that the
public source repositories and the digest manifest are not fully live yet. Neither pushes anything;
they document the verification story and the exact commands to execute it.

51
AERE-SECURITY-FIXES.md Normal file
View File

@ -0,0 +1,51 @@
# Aere Network: adversarial-review fixes (pre-deploy hardening)
Source review: `aerenew/docs/AERE-SECURITY-REVIEW-NEW-CONTRACTS.md`
Repo: `aerenew/contracts` (solc 0.8.23, OpenZeppelin v4.9.x). All contracts founder-gated, not deployed.
Guard pattern for L1 copied from `contracts/pqfinality/AereFinalityCertificateVerifier.sol` (`gateway.code.length == 0` revert).
## Per-finding status
### M1 (MEDIUM) FIXED, with proof
File: `contracts/erc8004/AereReputationRegistry8004.sol`
- Made the adapter `Ownable` (OZ v4, `@openzeppelin/contracts/access/Ownable.sol`, matching the repo).
- Added `mapping(address => bool) public isAuthorizedFeedbackAuthor`, owner functions `addFeedbackAuthor` / `removeFeedbackAuthor` (with `FeedbackAuthorSet` event and a zero-address guard on add), and a new error `NotAuthorizedFeedbackAuthor(address)`.
- Gated `giveFeedback`: first line now reverts `NotAuthorizedFeedbackAuthor` unless `isAuthorizedFeedbackAuthor[msg.sender]`. The rest of the flow (agent-registered, delta clamp, adapter-is-attestor) is unchanged.
- Design choice (documented in NatSpec): owner-curated author allowlist, mirroring the live AereAIReputation curated-attestor model. Scope is authorization-only: the owner cannot set a reputation value, cannot exceed the `{-1,0,+1}` delta clamp every author is held to, and moves no funds. No re-entrancy, no fund/reputation-value backdoor. Read path (`getScore`, `getStats`, `liveKeyOf`, `canWriteThrough`) untouched.
### L1 (LOW) FIXED (x3), with proof
Added the sibling's codeless-gateway guard to each verify path. New error `VerifierHasNoCode()` in each.
- `contracts/mpc/AerePQAggregateVerifier.sol` constructor: reverts if `gateway.code.length == 0` (after the existing `ZeroGateway` check).
- `contracts/depin/AereComputeMarketV3.sol` constructor: reverts if `zkVerifier.code.length == 0` (after the existing `ZeroAddress` check).
- `contracts/compliance/AereBitstringStatusList.sol` constructor: reverts if `nonRevocationVerifier_ != address(0) && nonRevocationVerifier_.code.length == 0`. The zero address is still allowed (keeps the zk non-revocation path fail-closed OFF until configured), which preserves existing behavior. Verifier is immutable, so the constructor is the correct place.
### L2 (LOW) FIXED, with proof
File: `contracts/agentic/AereVectorStore.sol`
- Bound each paid-query receipt to an intended provider. Added `address provider` to the `QueryReceipt` struct.
- `paidQuery` now takes `address provider` (validated non-zero via existing `ZeroAddress`) and records it in the receipt. Signature is now `paidQuery(storeId, queryHash, provider, agentId, amount, nonce, deadline, authSig)`. The settlement/forwarding flow (AERE402 rail, fee to sink, delta-measured forward to recipient) is unchanged; `receiptId` formula unchanged.
- `attestRetrieval` priced branch now reverts new error `NotReceiptProvider()` unless `msg.sender == receipt.provider`, so a third party (or the payer) cannot front-run the honest provider and burn the receipt with a junk resultRoot. `getReceipt` view extended to also return `provider`.
### L3 (LOW) DOCUMENTED (accepted risk), not code-changed
File: `contracts/erc8004/AereAP2MandateVerifier.sol`
- Chose DOCUMENT over CODE-CHANGE, per the task's guidance, because the two candidate code fixes are unsuitable: (a) a per-authorization nonce would break the intended standing-mandate ("sign once, spend repeatedly up to the cap") semantics that the design and tests rely on; (b) "bind consumption to the executor" is already the built-in `SETTLEMENT_EXECUTOR` gate.
- Strengthened the contract-level and `authorizeSpend` NatSpec: the open accounting path (`SETTLEMENT_EXECUTOR == address(0)`) is verification/test-only and MUST NOT be used in production; production MUST set `SETTLEMENT_EXECUTOR` to the AERE402 facilitator so a mandate's cap is only ever consumed alongside a real settlement. No funds at risk (this contract holds none); the vector is cap-exhaustion griefing only.
- The existing test "enforces the settlement-executor gate when one is configured" already proves the mitigation works (a non-executor is rejected, the configured executor succeeds). No new test needed.
## Tests added / changed
- `test/erc8004-adapters.test.js`: `deployRep` now authorizes `other` as a feedback author (both branches). Two pre-existing tests that relied on the ungated `giveFeedback` were updated to call as the authorized `other` (legitimate behavior change from the M1 gate). Added new test "gates giveFeedback to owner-authorized feedback authors (M1)": unauthorized caller reverts `NotAuthorizedFeedbackAuthor` and writes nothing, owner authorizes -> same call succeeds and moves the live score to 10, owner revokes -> re-blocked, and only the owner may manage the allowlist.
- `test/AerePQAggregate.test.js`: added "rejects a code-less SP1 gateway at construction (L1 fail-open guard)" (EOA gateway reverts `VerifierHasNoCode`, zero reverts `ZeroGateway`, real mock deploys).
- `test/AereComputeMarketV3.test.js`: added "construction guards" > "rejects a code-less ZK verifier at construction (L1)" (EOA reverts `VerifierHasNoCode`, zero reverts `ZeroAddress`, real mock deploys).
- `test/aere-pq-screen.test.js`: added "rejects a non-zero code-less non-revocation verifier at construction, but allows zero (L1)" (EOA reverts `VerifierHasNoCode`; zero deploys and `nonRevocationConfigured()==false`; real mock deploys).
- `test/aere-vector-store.test.js`: the two `paidQuery` calls updated to pass `provider.address`; the paid-query test extended to assert the receipt is bound to the provider and that a third party (`operator`) and even the payer (`consumer`) are rejected `NotReceiptProvider` with the receipt left unconsumed, then the bound provider consumes it successfully (L2 proof).
## Real re-run results (npx hardhat test <file>, from aerenew/contracts)
- test/erc8004-adapters.test.js .... 19 passing
- test/aere-vector-store.test.js ... 8 passing
- test/AerePQAggregate.test.js ..... 18 passing
- test/AereComputeMarketV3.test.js . 14 passing
- test/aere-pq-screen.test.js ...... 13 passing
`npx hardhat compile`: green (compiled successfully, then "Nothing to compile" on re-run).
## Notes
- No contracts outside the findings were touched. No existing guard was weakened. The L1 zero-address behavior for AereBitstringStatusList was intentionally preserved. Prose contains no em-dashes or "--".

View File

@ -0,0 +1,103 @@
# Adversarial Security Review: 17 pre-deployment contracts (Aere Network)
Scope: the new Solidity contracts under `aerenew/contracts/contracts/` listed in the task. All are founder-gated and NOT yet on mainnet. This is a report-only review (no fixes applied). Precompile/gateway calls (Falcon `0x0AE1`, SP1 gateway `0x9ca4…0628`) are mocked in tests; the real accept/reject branch was read directly in each contract.
Confidence tags: CONFIRMED (verified in source, and where relevant in tests) vs PLAUSIBLE (reasoned, not exercised). Severity reflects impact-if-triggered times likelihood.
## Headline
No CRITICAL and no HIGH issue was found. The fund-flow contracts (AereComputeMarketV3, AereDestinationSettler, AereAccountMigrator, AereVectorStore) are structurally sound: correct checks-effects-interactions, reentrancy guards on every external-call path, per-asset solvency accounting, no double-pay, no pay-without-proof, and correctly fail-closed Falcon/SP1 consumption. The most material finding is a MEDIUM reputation-integrity gap in the ERC-8004 reputation adapter. The remaining issues are LOW (deployment-conditional fail-open, and two griefing vectors) or INFO.
## Findings table (most severe first)
| ID | Severity | Contract | Function | Issue | Confidence |
|----|----------|----------|----------|-------|------------|
| M1 | MEDIUM | AereReputationRegistry8004 | `giveFeedback` | No caller gate. Once the adapter is an authorized attestor, ANY address can inflate/tank any registered agent's reputation, defeating the live contract's curated-attestor model and its evidence-dedup. | CONFIRMED |
| L1 | LOW | AerePQAggregateVerifier (+ AereComputeMarketV3, AereBitstringStatusList) | constructor / verify paths | SP1 gateway / verifier address is not asserted to have code. `verifyProof` returns void, so a codeless gateway makes the call succeed silently (no extcodesize check) = fail-open. Sibling AereFinalityCertificateVerifier guards this; these do not. | CONFIRMED (code); PLAUSIBLE (needs misdeploy) |
| L2 | LOW | AereVectorStore | `attestRetrieval` | A paid query receipt is not bound to a specific provider. Any address can consume a victim's receipt with a junk `resultRoot`, burning the honest provider's one-shot attestation and wasting the payment. | CONFIRMED |
| L3 | LOW | AereAP2MandateVerifier | `authorizeSpend` | In the open accounting path (`SETTLEMENT_EXECUTOR == 0`), the Falcon signature is a standing authorization that anyone can replay to consume a mandate's cap without a real settlement (cap-exhaustion griefing). | CONFIRMED |
| I1 | INFO | AereDestinationSettler | `fill` | A fee-on-transfer / rebasing output token delivers less than the recorded `deliveredAmount`; the recipient can receive under the DECLARED output even though `deliveredAmount >= declared`. | PLAUSIBLE |
| I2 | INFO | AereBitstringStatusList | `publishStatusRoot` / `verifyNonRevocation` | The status root is controller-attested (self-reported); a dishonest issuer can publish a root that lies about its own revocations. Documented `[MEASURE]`; `getWord` allows independent recomputation. | CONFIRMED (by design) |
| I3 | INFO | AereVectorStore | `paidQuery` | Uses raw `IERC20.transfer` with a bool check; a no-return (USDT-style) settlement token makes paidQuery revert (fail-closed, but a compat foot-gun; deposit side is delta-measured and robust). | PLAUSIBLE |
| I4 | INFO | AereRecoveryRegistry | `getRecord` | Reverts with `UnknownOperator(recordId)` on a bad record id (wrong error label; cosmetic). | CONFIRMED |
| I5 | INFO | AereValidationRegistry8004 | `requestValidation` | Permissionless request creation allows storage-spam; harmless (no funds, validator opts in to respond). | CONFIRMED |
## Per-finding detail
### M1 (MEDIUM) — AereReputationRegistry8004.giveFeedback: permissionless reputation writes / sybil
`giveFeedback(uint256 agentId, int8 delta, bytes32 evidenceHash, string evidenceURI)` (lines 103-114) has no `msg.sender` restriction. It checks only: agent registered, `delta in {-1,0,1}`, and that the adapter itself is an attestor on the live `AereAIReputation`. It then forwards `REPUTATION.attest(operator, agentKey, delta, evidenceHash, evidenceURI)` with the adapter as the on-chain attestor.
The live `AereAIReputation` (read at `agentic/AereAIReputation.sol`) deliberately gates `attest` to a curated, immutable attestor set and adds anti-abuse: no self-attest and dedup by `(attestor, evidenceHash)`. Routing through the adapter collapses all callers into ONE attestor identity, so:
- the `(attestor, evidenceHash)` dedup is defeated because an attacker supplies a fresh `evidenceHash` per call;
- `positiveCount` / `disputeCount` can be driven arbitrarily by any address.
`scoreOf = positiveCount*10 - ... - disputeCount*20` clamped to `[0,10000]`. So an attacker can push any registered agent to `MAX_SCORE` (self-inflate a scam agent) or to `0` (tank a competitor). The contract's own NatSpec notes reputation gates economic routing (e.g. "AERE402 require >= 3000 before routing"), so manipulation has downstream financial consequence.
Exploit: attacker calls `giveFeedback(victimAgentId, -1, keccak(random_i), "")` in a loop (or `+1` to inflate a malicious agent). The test `erc8004-adapters.test.js` demonstrates the enabling behavior directly: `repAdapter.connect(other).giveFeedback(...)` succeeds from a non-Foundation, non-operator signer (lines 292, 306-308).
Condition / mitigation: this bites only when the adapter is registered as an attestor on the reputation instance. The currently-live `AereAIReputation` attestor set is immutable and does not include the adapter, so write-through requires deploying a NEW reputation instance listing the adapter (the documented, founder-gated intended path, `deployRep(true)` in the test). Since that IS the intended production posture for write-through, the flaw is real.
Fix direction: gate `giveFeedback` to an authorized feedback-submitter set (or a per-interaction proof, e.g. an AERE402 settlement receipt binding the caller to a real interaction), so the adapter cannot act as an open reputation firehose. If permissionless feedback is intended, the score must not be treated as sybil-resistant by any downstream gate.
### L1 (LOW) — Verifier constructors do not assert the gateway has code (fail-open if misdeployed)
`ISP1Verifier.verifyProof(...)` is declared `external view` with NO return value (`zkverify/ISP1Verifier.sol`). For an external call to a void-returning function, Solidity omits the `extcodesize` check, so a call to a codeless address SUCCEEDS silently. A verifier that relies on "verifyProof reverts on an invalid proof" therefore fails OPEN if its gateway address has no code.
`AereFinalityCertificateVerifier` guards exactly this: its constructor reverts `VerifierHasNoCode` when `gateway.code.length == 0` (line 199, with a NatSpec comment naming the silent-accept risk). The following do NOT:
- `AerePQAggregateVerifier` constructor (lines 187-192): checks only `gateway != 0` and `vkey != 0`. A codeless gateway makes `verifyAggregate` accept every committee authorization = post-quantum auth bypass for any account bound via `AerePQAggregateModule`.
- `AereComputeMarketV3` constructor (lines 213-218): checks only `zkVerifier != 0`. A codeless `ZK_VERIFIER` makes `submitResultZK` pay the provider with NO valid proof (fund loss). Highest impact of the three.
- `AereBitstringStatusList.verifyNonRevocation` (lines 304-308): `try NON_REVOCATION_VERIFIER.verifyProof {...} return true`. A non-zero-but-codeless verifier returns true for any proof (a revoked credential proves "not revoked").
Severity is LOW because the gateway is an IMMUTABLE constructor argument set by the founder-gated deployer, and the correct value (`0x9ca4…0628`) has code; the fail-open only occurs on deployer error. Impact-if-triggered is severe (fund loss / auth bypass), so this is a should-fix consistency gap rather than a runtime vulnerability.
Fix direction: add the same `if (gateway.code.length == 0) revert` guard used by `AereFinalityCertificateVerifier` to `AerePQAggregateVerifier`, `AereComputeMarketV3`, and `AereBitstringStatusList` (or require a successful known-answer call at construction).
### L2 (LOW) — AereVectorStore: paid-query receipt not bound to the attesting provider
`paidQuery` mints a receipt keyed by `keccak(this, storeId, queryHash, agentId, nonce)` and forwards the net payment to `paymentRecipient`. `attestRetrieval` (lines 344-381) then consumes the receipt with only these checks: exists, not consumed, `storeId`/`queryHash` match, `amount >= queryPrice`. There is NO check that `msg.sender` is any particular provider.
Because `receiptId`, `storeId` and `queryHash` are all public (derivable from the `QueryPaid` event / inputs), any address can front-run the honest retrieval provider and call `attestRetrieval` with a garbage `resultRoot`, flipping `consumed = true`. The honest provider then reverts `ReceiptConsumed` and cannot attest, so the payer paid but obtains no trustworthy attestation (must pay again).
No funds are stolen (the payment already reached `paymentRecipient`), so this is griefing / service-denial, LOW. Fix direction: bind the receipt to an intended provider (recorded at paidQuery or asserted via signature), or record attestations per-provider rather than one-shot per receipt.
### L3 (LOW) — AereAP2MandateVerifier: replayable cap-consumption in the open path
`authorizeSpend` records cumulative spend under `mandateHash` and enforces the cap. The `mandateHash` does NOT include `amount`, so a valid `falconSig` is a standing authorization: each `authorizeSpend` call consumes `amount` from the cap. When `SETTLEMENT_EXECUTOR == address(0)` (the "open accounting path", used in the test at lines 160-163 and 491-492 where one `sig` authorizes two spends), the call is permissionless. An attacker who observes the public `falconSig` can replay `authorizeSpend` to exhaust the mandate's remaining cap without any real settlement, blocking the agent's legitimate spends under that mandate.
The design already contains the mitigation: setting `SETTLEMENT_EXECUTOR` to the AERE402 facilitator makes `authorizeSpend` callable only alongside real settlement (verified by the gated test at lines 542-560). Severity LOW: griefing only, no custody, and preventable by the intended production config. Fix direction: never deploy production with `SETTLEMENT_EXECUTOR == 0`; document the open path as test-only, or add a per-authorization nonce.
### INFO items
- I1: `AereDestinationSettler.fill` records `outputAmount = deliveredAmount` but transfers solver -> recipient directly; a fee-on-transfer token shorts the recipient below the declared output. The user chose the output token, and origin-side repayment is against the locked input, so no protocol fund loss; worth documenting as a supported-token constraint.
- I2: `AereBitstringStatusList` status roots are controller-attested (self-reported); a dishonest issuer can misrepresent its own revocation state. Explicitly marked `[MEASURE]`; `getWord` exposes raw bits for independent recomputation.
- I3: `AereVectorStore.paidQuery` uses raw `transfer` with a bool check; a no-return ERC-20 would make it revert (fail-closed compat foot-gun). Deposit side is delta-measured, so no accounting drift.
- I4: `AereRecoveryRegistry.getRecord` reverts with `UnknownOperator` on a bad record id (mislabeled error, cosmetic).
- I5: `AereValidationRegistry8004.requestValidation` is permissionless (storage spam only; harmless).
## Per-contract verdict
| Contract | Verdict |
|----------|---------|
| pqfinality/AerePQAttestationKeyRegistry.sol | CLEAN. Append-only enforced (rotation flags predecessor, never overwrites); validators register only their own key (`msg.sender == validator`); owner curates membership but cannot forge keys; deterministic root. |
| pqfinality/AereFinalityCertificateVerifier.sol | CLEAN. Correctly fail-closed; binds root/size/domain/quorum to the live registry; `ceil(2N/3)` threshold math verified (N=7->5, 9->6, 4->3); includes the `VerifierHasNoCode` guard the siblings lack. |
| mpc/AerePQAggregateVerifier.sol | ISSUE (L1). Verify logic is fail-closed and correct; constructor missing the codeless-gateway guard. |
| modular/AerePQAggregateModule.sol | CLEAN. Per-account binding via singleton `msg.sender`; fail-closed try/catch decode; delegates to the fail-closed verifier. |
| compliance/AereTrustRegistry.sol | CLEAN. Owner-gated (trusted list operator by design); append-only status/scope history; terminal revocation truly terminal (no transition out of Revoked); fail-closed accreditation and PQC views. |
| compliance/AereVerifiableCredential.sol | CLEAN. Digest binds chainId+contract+all fields (no replay); anchor verifies full path fail-closed; `isValid` re-checks live accreditation/validity/revocation. |
| compliance/AereBitstringStatusList.sol | CLEAN (I2 by-design trust caveat; L1 non-revocation path shares the codeless-verifier gap). Revocation monotonic/terminal; suspension reversible; verifyNonRevocation binds chain/contract/list/purpose + freshness. |
| intents/AereDestinationSettler.sol | CLEAN (I1 note). No custody (solver->recipient direct), exact output-match, at-most-once fill, CEI + nonReentrant, first-filler-wins with no loss to the loser (AlreadyFilled before transfer). |
| depin/AereComputeMarketV3.sol | CLEAN (L1 ZK-verifier deploy note). No double-pay (single terminal status per job), no pay-without-proof (settle after `verifyProof`), per-asset solvency tracked, fee-on-transfer rejected at deposit, Falcon settlement fail-closed, native+ERC20 liability accounting correct (no double-count on native jobs). |
| agentic/AereVectorStore.sol | ISSUE (L2). paidQuery settlement is CEI + nonReentrant and correctly gated; commit is append-only + fail-closed Falcon; the gap is receipt-to-provider binding. |
| pqc/AereAccountMigrator.sol | CLEAN. Pure conduit: caller is always `from`, caller-chosen `destination` is always `to`; no owner/sweep; strict native handling (StrayNative); atomic (SafeERC20 reverts roll back); nonReentrant. |
| oracle/AereRandomnessBeaconV2.sol | CLEAN. Real EIP-2537 BLS12-381 pairing verification, fail-closed end to end (empty/short/wrong-length returns -> false), on-curve + subgroup (precompile-enforced) checks; no path returns true without pairing equality; honest replacement of a stub verifier. |
| AereRecoveryRegistry.sol | CLEAN (I4 cosmetic). Append-only, per-operator monotonic nonce bound into the signed record (no replay), Falcon verification fail-closed, no admin/owner. |
| erc8004/AereIdentityRegistry8004.sol | CLEAN. Metadata binding gated to the live Falcon-root controller; domain uniqueness enforced; no cross-agent privilege. |
| erc8004/AereReputationRegistry8004.sol | ISSUE (M1). Read path is a faithful pass-through; the write path lacks caller authorization. |
| erc8004/AereValidationRegistry8004.sol | CLEAN (I5 spam note). Challenge binds request tuple + response (non-replayable), respond-once, PQC verification fail-closed, active-key re-check at response time. |
| erc8004/AereAP2MandateVerifier.sol | ISSUE (L3, open-path only). mandateHash binds chain+contract+fields; cap overflow-safe; window + active-key + Falcon checks fail-closed; the gap is the open accounting path being replayable. |
## Coverage-gap notes (what the tests do NOT exercise)
- No test drives any verifier with a codeless gateway (L1) - both `AerePQAggregate.test.js` and `AerePQFinalityCertificate.test.js` only use `MockSp1Verifier`, which has code, so the missing-guard divergence is invisible to the suite.
- `erc8004-adapters.test.js` exercises `giveFeedback` from an arbitrary signer as EXPECTED behavior (M1), so the missing caller gate is treated as a feature, not tested as a risk.
- The AP2 open-path signature replay (L3) is exercised as intended (one sig, two spends) without a test asserting that an unrelated caller should be blocked in the open path.
- No test covers `attestRetrieval` being called by an address other than the intended provider (L2).

View File

@ -0,0 +1,63 @@
# Spec 13: Self-verifying parallel Shadow Block Producer
Date: 2026-07-19. Chain 2800 (Aere Network mainnet), head ~10,364,611 at build time.
## What was built
A live compliance monitor under `aerenew/shadow-producer/` that re-derives each
canonical chain-2800 block independently and raises a DIVERGENCE ALARM on any
mismatch. It never produces the canonical chain (read-only monitor).
Files:
- `aerenew/shadow-producer/shadow-producer.mjs` - the runner (real, runnable).
- `aerenew/shadow-producer/blockstm-adapter.mjs` - Phase C [MEASURE] scaffold seam
to the Block-STM parallel executor.
- `aerenew/shadow-producer/README.md` - overview + honest scope + run instructions.
- `aerenew/shadow-producer/package.json` - type:module, scripts, no deps (ethers
resolved from repo installs via ../conformance/lib.mjs, same as the conformance suite).
- `aerenew/shadow-producer/logs/` - captured live run logs (monitor, selftest, reexec).
- `aerenew/docs/AERE-SHADOW-PRODUCER.md` - architecture + honest-status design doc.
## Phases
- Phase A [FRESH, RUNS LIVE]: recompute QBFT block hash from header fields, assert ==
canonical blockHash; recover committed seals to quorum against the validator set
AS OF THAT BLOCK (set grew N=3 -> N=7 over history; per-block set is the honest check).
- Phase B [FRESH, RUNS LIVE]: for a post-EIP-2935 empty block the only state delta is
one history-ring SSTORE, history[(n-1) % 8191] = blockhash(n-1); re-derive and assert.
Verifiable for recent blocks (ring holds last 8191; public RPC non-archival).
- Phase C [MEASURE, SCAFFOLD]: full parallel re-execution of the block's txs through the
Block-STM executor -> full world-state root, diff vs canonical stateRoot. Needs a
full-state node + forked-Besu parallel EVM; adapter emits an inspectable plan + the
exact command. Faithful backend = aerenew/parallel/ (253 real blocks re-imported, 0
mismatch), NOT the toy Rust executor (which proves concurrency correctness only).
## REAL run result (FRESH)
`node shadow-producer.mjs` against https://rpc.aere.network, chain 2800:
- 29 blocks Phase A: block hash re-derived byte-for-byte at EVERY block, spanning the
whole chain incl. EIP-2935 activation (9,189,161) and base-fee floor fork (10,141,734);
per-block seal quorum verified (2/3 early N=3, 5/7 current N=7).
- 17 EIP-2935 empty-block state-delta re-derivations verified (Phase B).
- 75 total re-derivation assertions, 0 divergences, exit 0.
- Non-vacuity self-test (`--selftest`): genuine block re-derives (PASS); one-byte
stateRoot tamper is CAUGHT (PASS). Proves the alarm is not a rubber stamp.
- Phase C (`--reexec`): emits [MEASURE] re-execution plans honestly (no fabricated root).
## Flags / honesty
- Parallel execution is NOT live on L1; canonical chain is sequential Besu. Stated
everywhere.
- Monitor never produces the canonical chain; a bug in it cannot affect consensus
(writes nothing consensus reads). Zero-risk on-ramp reasoning documented.
- Consensus stays classical secp256k1 ECDSA QBFT; not touched, not post-quantum.
- Full parallel re-execution against real world state = [MEASURE]; block-fetch +
hash/state-delta re-derivation = [FRESH] and actually ran.
- No em-dashes / no "--" in prose (only markdown rules, tables, ASCII diagram, CLI in
code fences). Brand "Aere Network"/"Aere" title case; ticker AERE uppercase.
- One correctness fix during build: initial seal check wrongly used the current N=7
quorum on historical blocks -> false alarms; fixed to per-block validator set. This
itself demonstrated the alarm firing, then passing cleanly once correct.
- Reused existing hashing/RLP/seal helpers from aerenew/conformance/lib.mjs (did not
reinvent). Wired to existing evidence (Block-STM, cross-client determinism, 253-block
re-import) rather than inventing new numbers.

302
AERE-SHADOW-PRODUCER.md Normal file
View File

@ -0,0 +1,302 @@
# Aere Shadow Block Producer
Technical specification. Chain 2800 (Aere Network mainnet).
Grounding and consistency: this document describes a live compliance monitor that
generalizes the offline determinism proofs already in the repository. It is
consistent by construction with `aerenew/parallel-executor/README.md` (the
Block-STM correctness guarantee), `aerenew/parallel/combined-fork-binary/VALIDATION_REPORT.md`
(the forked-Besu commit path, 253 real blocks re-imported with zero state-root
mismatch), and `aerenew/docs/AERE-CROSS-CLIENT-DETERMINISM.md` (two independent
clients computing byte-identical block hashes and state roots). Where those
documents and this one describe the same status, they say the same thing. If they
ever diverge, the cross-client determinism doc and the engineering security spec
are authoritative and this document is wrong.
The runnable component lives in `aerenew/shadow-producer/` (`shadow-producer.mjs`,
`blockstm-adapter.mjs`, and its `logs/`).
---
## 1. Why a shadow producer, and the honest scope
### 1.1 The gap between an offline proof and a live guarantee
Aere already has strong offline evidence that parallel execution is safe. The
Block-STM executor is proven bit-identical to sequential execution over 6000
randomized comparisons (`aerenew/parallel-executor/`), the forked-Besu commit path
re-imported 253 real exported blocks with zero state-root mismatch
(`aerenew/parallel/`), and two independent clients compute byte-identical block
hashes and state roots (`aerenew/docs/AERE-CROSS-CLIENT-DETERMINISM.md`). Each of
those is an offline, bounded, after-the-fact proof.
What none of them is, on its own, is a continuously armed live check that watches
the canonical chain as it advances and asserts, block by block, that an independent
re-derivation still agrees. The cross-client determinism doc names exactly this gap
in its section 4: the cross-check "is not yet wired into production monitoring as an
automated, alerting fail-safe". The shadow producer is the component that closes it.
### 1.2 What it is, in one sentence
> A read-only process that re-derives each canonical chain-2800 block independently
> and raises a divergence alarm the moment its re-derivation disagrees with what the
> live sequential Besu producer committed.
It is a monitor. It runs alongside the chain, never in front of it.
### 1.3 What this does NOT claim (read this, it is load-bearing)
- It does NOT claim parallel execution is live on Aere L1. It is not. The canonical
chain is produced SEQUENTIALLY by the forked Hyperledger Besu producer. Nothing in
this component changes how the L1 executes blocks.
- It does NOT produce the canonical chain. The shadow producer seals no block, casts
no consensus vote, and gossips nothing. It issues only read-only `eth_*` and
`qbft_*` calls. It cannot affect consensus, by construction, because it writes
nothing that consensus reads.
- It does NOT change the consensus model. Consensus stays classical secp256k1 ECDSA
QBFT. It is not post-quantum. This monitor does not touch that surface.
- It does NOT, by itself, authorize enabling parallel execution on L1. That decision
stays founder gated and audit gated. This component is the evidence-gathering step
that would inform such a decision, not the decision.
The value delivered is real and bounded: an independent, live, continuously
runnable re-derivation of the canonical chain whose disagreement with the producer
is detectable rather than silent.
---
## 2. Architecture
The pipeline is four stages: fetch canonical, re-derive independently, diff, alarm.
```
live chain 2800 (sequential Besu producer, ECDSA QBFT)
|
| read-only eth_getBlockByNumber / eth_getStorageAt / qbft_getValidatorsByBlockNumber
v
+-------------------- shadow producer (monitor) --------------------+
| |
| Phase A recompute QBFT block hash from header fields |
| recover committed seals -> quorum (set as-of-block) |
| |
| Phase B re-derive the empty-block state delta |
| history[(n-1) % 8191] == blockhash(n-1) (EIP-2935) |
| |
| Phase C replay transactions through the Block-STM parallel |
| executor -> full world-state root [MEASURE] |
| |
+----------------------------- diff --------------------------------+
|
| any mismatch
v
DIVERGENCE ALARM (halt-and-investigate signal; never forks the chain)
```
### 2.1 Phase A: structural re-derivation of the block hash [FRESH]
For each monitored block the shadow producer fetches the canonical header, then
recomputes the QBFT on-chain block hash from the header fields using the same
`EXCLUDE_COMMIT_SEALS_AND_ROUND_NUMBER` extraData re-encoding that the Besu fork
uses (`BftBlockHashing`), and asserts the recomputed hash equals the RPC-reported
`blockHash`. It then recovers each 65-byte committed seal to a distinct validator
address and requires the recovered-signer count to meet the QBFT quorum
`ceil(2N/3)` for the validator set VALID AT THAT BLOCK.
The per-block validator set matters and is a real correctness point. The chain-2800
validator set grew over its history (N=3 in the early range, N=7 today), so a seal
quorum check that used the current set on a historical block would be wrong. The
monitor queries `qbft_getValidatorsByBlockNumber` at each block's height and checks
seals against that set with that block's quorum.
This phase proves the fetch plus consensus-hashing path is byte-correct. It runs
live and read-only with no client build. It reuses the exact byte layouts from
`aerenew/conformance/lib.mjs`, so it is consistent with the executable conformance
suite by construction.
### 2.2 Phase B: empty-block state-delta re-derivation [FRESH]
Organic chain-2800 blocks are near-empty. For a post-EIP-2935 empty block the ONLY
state transition is a single system SSTORE into the EIP-2935 history ring: the
parent block hash is written to slot `(n-1) % 8191` of the history contract
`0x0000F90827F1C53a10cb7A02335B175320002935`. The shadow producer re-derives that
one delta from first principles (the parent hash, the ring index) and asserts it
matches the value the history contract actually stores. This is a real, if partial,
re-derivation of the block's committed state delta, and it runs live and read-only.
It is verifiable only inside the recent window: the ring holds the last 8191 hashes
at `latest`, and the public RPC is not archival, so historical slots are legitimately
out of window and are reported as skipped, not as divergences. Pre-EIP-2935 empty
blocks (before block 9,189,161) have a constant state root and are skipped as
pre-fork. This behavior was measured directly (empty pre-fork blocks 1000..1005 share
one state root; each post-fork empty block advances the state root by exactly the one
ring write), and the monitor encodes that measured fact.
### 2.3 Phase C: full parallel re-execution [MEASURE]
The complete check is: take the parent world state as of block n-1, replay block n's
transactions through the Block-STM parallel executor, recompute the full
Merkle-Patricia world-state root, and diff it against the canonical `stateRoot` in
the header. This is the strong check, because a matching state root means every
transaction, reward, and fee rule was applied identically.
It is honestly marked [MEASURE], because it needs infrastructure the read-only
monitor does not carry: a full-state (archive) node holding the parent world state,
and a client-grade parallel EVM. `blockstm-adapter.mjs` produces an inspectable
re-execution PLAN for each block (transaction set, parent state root needed,
canonical state root to match, and the exact command a full-state harness would run)
without pretending the heavy step has happened.
There are two backends, and the adapter is explicit about which is which:
- The standalone Rust Block-STM (`aerenew/parallel-executor/`) proves the
CONCURRENCY CONTROL is serializable, but its state model is balances plus
key/value slots with a fixed transaction-kind set and a flat keccak root, not the
full EVM with an MPT. It is the right tool to prove the scheduler is correct and
the wrong tool to reproduce an L1 state root. The adapter deliberately does not
map arbitrary EVM calldata onto that toy model, because that translation would be
unfaithful.
- The forked-Besu Block-STM commit path (`aerenew/parallel/`) is the client-grade
backend: the real Besu EVM plus the parallel executor and commit. It already
re-imported 253 real exported blocks with zero state-root mismatch. A faithful