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 e4cead319d
61 changed files with 12617 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,480 @@
# 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
`aere-client2` (a dedicated second-client host, Helsinki, dedicated firewall,
JSON-RPC bound to `127.0.0.1` only, durable `systemd` 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 Hetzner infrastructure, 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 the live infra box.
- `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 the build box, 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 the build box at `/root/work/probe/` and `/opt/aere/besu-pqc-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 `/root/besu` 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
Phase C reproduction of a live block's state root runs on that path, on a scratch
full-state node, and NEVER on the live producer or the infra box.
---
## 3. Why it is a zero-risk on-ramp to L1 parallel execution
The shadow producer is the safest possible way to accumulate real, live evidence
about parallel execution, for one structural reason: it runs alongside the chain and
never produces it.
- It writes nothing on-chain. It seals no block and casts no vote. Consensus never
reads anything the monitor produces, so a bug in the monitor, up to and including a
wrong re-derivation, cannot change the canonical chain, cannot fork it, and cannot
halt it. The worst a monitor bug can do is raise a false alarm, which is a
human-investigated signal, not a consensus action.
- It is read-only against the RPC. It issues only `eth_*` and `qbft_*` reads. It
holds no validator key and touches no producer path.
- Its failure posture is halt-and-investigate, not fork. A detected divergence is a
signal to stop trusting the divergent block until a human has looked, exactly the
posture the cross-client determinism doc records for the chain itself (zero forks
observed in all fault testing; the chain halts and later recovers rather than
forking).
The on-ramp is therefore: run the monitor live and read-only for as long as wanted,
gathering a continuous record that an independent parallel re-derivation agrees with
the sequential producer block by block, at zero risk to the live chain. Only after
that evidence exists, and only behind the founder and audit gates, could enabling
parallel execution on the actual producer ever be proposed. The monitor is the
evidence; it is not the flip, and running it does not bring the flip any closer than
the founder and the auditors decide it does.
---
## 4. Relationship to the existing evidence
The shadow producer does not reinvent any determinism proof. It is the live consumer
and generalization of three existing artifacts.
- Block-STM correctness (`aerenew/parallel-executor/`). The executor is proven
bit-identical to sequential execution. That is the property that makes a parallel
re-derivation a valid check at all: if the parallel executor could disagree with
sequential on correct inputs, its "divergence" would be meaningless. Because it
cannot, a Phase C divergence would point at the chain, not at the checker.
- Forked-Besu commit path (`aerenew/parallel/`). The 253-block zero-mismatch
re-import is the offline precedent for Phase C. The shadow producer generalizes
that from "re-import a fixed export and check the roots" to "re-derive live blocks
as they arrive and check the roots continuously".
- Cross-client determinism monitor (`aerenew/docs/AERE-CROSS-CLIENT-DETERMINISM.md`).
That work compares two INDEPENDENT CLIENTS (patched Besu and patched Nethermind)
and shows they compute the same state. The shadow producer is a complementary axis:
it compares one client's SEQUENTIAL production against a PARALLEL re-derivation of
the same execution. Cross-client determinism guards against an implementation
monoculture; the shadow producer guards against a sequential-versus-parallel
execution divergence. Both share the same honest boundary language and the same
halt-not-fork failure posture, and both are, today, cross-checks rather than
automated always-armed gates.
The two monitors are designed to converge: the endpoint is a single supervised
process that reads the chain at head, runs cross-client and sequential-versus-parallel
re-derivations, and alarms on the first byte of divergence.
---
## 5. Honest status
**PROVEN, live and read-only (Phase A and Phase B).** On the latest run (2026-07-19,
chain 2800 at head near 10,364,611), the shadow producer re-derived the canonical
block hash of every monitored block, byte-for-byte, across a spread covering the
whole chain including the EIP-2935 activation (block 9,189,161) and the base-fee
floor fork (block 10,141,734), plus a recent contiguous window at the live edge. It
recovered committed seals to quorum against the validator set valid at each block
(2 of 3 on early N=3 blocks, 5 of 7 on current N=7 blocks). It re-derived the
EIP-2935 empty-block state delta for 17 recent blocks. Divergences: zero. The
non-vacuity self-test passes: a genuine block re-derives, and a one-byte tamper of a
hashed header field is caught. Raw logs are in `aerenew/shadow-producer/logs/`.
**[MEASURE], the full parallel re-execution (Phase C).** Reproducing a live block's
full world-state root by replaying its transactions through the forked-Besu parallel
executor against real parent state is scaffolded, not run here. It needs a full-state
node and the client-grade parallel EVM, on a scratch box, never on the live producer.
The adapter emits the plan and the exact command; the offline precedent is the
253-block zero-mismatch re-import in `aerenew/parallel/`.
**NOT a producer, NOT a live automated gate, NOT L1 parallel execution.** The shadow
producer produces nothing on the canonical chain. It is a monitor that is run and
checked; it is not yet wired as a continuously armed, always-alerting production
fail-safe, and the L1 producer remains sequential Besu. Enabling parallel execution
on L1 stays a separate, founder-gated, audit-gated change, not started and not
authorized by this document.
Public-copy rule that travels with this status: the shadow producer is a read-only
compliance monitor that independently re-derives canonical chain-2800 blocks. Do not
write "parallel execution is live", "parallel L1", or anything implying the monitor
produces or seals blocks. Never claim mainnet consensus is post-quantum; it is
classical secp256k1 ECDSA QBFT.
---
## 6. Reproduction
Read-only against the live reference; needs only ethers v6, resolved from the repo's
existing installs (no `npm install` in this directory).
```bash
cd aerenew/shadow-producer
# default monitor: spread across the chain + recent window
node shadow-producer.mjs
# a contiguous range, or the last K blocks at the live edge
node shadow-producer.mjs --from 1 --to 2000
node shadow-producer.mjs --recent 32
# emit the Phase C [MEASURE] re-execution plans alongside A and B
node shadow-producer.mjs --recent 4 --reexec
# non-vacuity self-test: genuine re-derives, tamper is caught
node shadow-producer.mjs --selftest
```
Exit 0 means every checked block re-derived identically. Exit 1 means a divergence
alarm fired (or an error). For the [MEASURE] Phase C, follow the printed plan on a
full-state scratch node running the forked-Besu parallel executor from
`aerenew/parallel/`; assert the recomputed state root equals the canonical header
value, exactly as the 253-block re-import did offline.
---
## 7. Summary
The shadow block producer turns Aere's offline determinism proofs into a live,
read-only monitor that re-derives each canonical chain-2800 block and would raise a
divergence alarm on the first mismatch. Today it proves, live, that the block hash
and the empty-block state delta re-derive identically to the canonical chain, with a
non-vacuous alarm; the full parallel re-execution against real world state is
scaffolded and marked [MEASURE], with a 253-block zero-mismatch offline precedent.
It is a zero-risk on-ramp precisely because it never produces the canonical chain: it
runs alongside, writes nothing consensus reads, and fails to halt-and-investigate
rather than to fork. The canonical chain is still produced sequentially by Besu,
parallel execution is not enabled on L1, and enabling it stays founder and audit
gated. This monitor is the evidence, not the flip.

View File

@ -0,0 +1,121 @@
# Component (e): GENERIC AIR quotient-consistency check for STARK-verify precompile 0x0AE8
> **Scope caveat (2026-07-19 finding).** This generic DEEP-ALI quotient check is conformance-confirmed
> against Plonky3 `p3-uni-stark` `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against
> SP1 6.1.0. SP1 6.1.0 is a **Hypercube** release (KoalaBear multilinear), and its inner recursion
> proof uses a **sumcheck-based zerocheck plus LogUp-GKR, not a DEEP-ALI univariate quotient check**,
> so this generic quotient mechanism (one of the two largest confirmed pieces) does NOT apply to SP1
> 6.1.0 at all and this work does not verify SP1 6.1.0 proofs. Note the "six generic components
> confirmed" claim below means a BabyBear+FRI STARK verifier skeleton, NOT an SP1 6.1.0 verifier.
> Replacing the SP1 BN254 Groth16 wrap is a separate ~22 to 32 person-week retarget. See
> `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Scope: implement + conformance-confirm the GENERIC AIR constraint / quotient
consistency MECHANISM (the final generic piece of a BabyBear STARK verifier), keep the real SP1-proof
path fail-closed.
## Bottom line
- The GENERIC AIR quotient-consistency MECHANISM (component (e)'s DEEP-ALI identity) is now IMPLEMENTED
and CONFORMANCE-CONFIRMED against the pinned Plonky3 `p3-uni-stark` 0.4.3-succinct via a REAL known
answer test. KAT: `pqc-fork/pq-stark/test_air_quotient.py` PASS=18 FAIL=0 (Python + Node + standalone
Java byte-identical). Accept genuine + reject tampered, on two known example AIRs.
- With this, all SIX generic components of a BabyBear STARK verifier skeleton (a, b, c, d, e-generic, f)
are conformance-confirmed against the pinned crates.
- The top level STAYS FAIL-CLOSED for real SP1 proofs. The SP1-recursion-SPECIFIC AIR (its exact
constraint set + interactions + public-value layout) and the vkey digest that binds it are NOT ported
(`StarkConstraints.SP1_RECURSION_AIR_PORTED = false`), so `StarkConstraints.evaluateAtZeta` returns
UNAVAILABLE on the real path and ACCEPT is unreachable. VERIFIED by a runtime check that drives the real
precompile classes (`computePrecompile` returns EMPTY for a well-formed AS1 wire input).
## 1. The exact GENERIC quotient identity, confirmed from pinned source
Traced from `p3-uni-stark` 0.4.3-succinct `verifier.rs` (lines 90-141), `p3-commit` `domain.rs`
(`TwoAdicMultiplicativeCoset`), and `p3-air` `folder.rs` / `air.rs` (`VerifierConstraintFolder`):
- Trace domain: `TwoAdicMultiplicativeCoset { log_n = degree_bits, shift = 1 }`, g = two_adic_generator(degree_bits).
- `log_quotient_degree = log2_ceil(max_constraint_degree - 1)`; `quotient_degree = 1 << log_quotient_degree`.
- Quotient domain (`create_disjoint_domain`): `log_n = degree_bits + log_quotient_degree`,
`shift = trace.shift * Val::generator() = GENERATOR` (= 31, confirmed via the extractor's `val_generator`).
Chunk i (`split_domains`): `log_n = degree_bits`, `shift = GENERATOR * two_adic_generator(degree_bits+log_quotient_degree)^i`.
- alpha = `sample_ext_element()` after observing the trace commitment; zeta = `sample()` after observing
the quotient-chunks commitment; zeta_next = zeta * g.
- Selectors at zeta (`selectors_at_point`, shift=1): `z_h = zeta^(2^degree_bits) - 1`;
`is_first_row = z_h/(zeta-1)`; `is_last_row = z_h/(zeta - g^-1)`; `is_transition = zeta - g^-1`;
`inv_zeroifier = z_h^-1`.
- Quotient reconstruction: `zps[i] = prod_{j!=i} zp_j(zeta) * zp_j(chunk_i.first_point())^-1`, where
`zp_D(pt) = (pt*shift_D^-1)^(2^log_n_D) - 1`; then
`quotient(zeta) = sum_i zps[i] * sum_e monomial(e) * quotient_chunks[i][e]`, monomial(e)=x^e in F_{p^4}.
- Constraint fold (Horner in `VerifierConstraintFolder.assert_zero`): `acc = acc*alpha + constraint`, over
the AIR `eval()` emission order; `when_first_row`/`when_transition`/`when_last_row` multiply the
constraint by the corresponding selector. Rows: `trace_local` (zeta), `trace_next` (g*zeta).
- The identity (verifier.rs 137-141): `folded_constraints * inv_zeroifier == quotient`, i.e.
`folded_constraints(zeta) == Z_H(zeta) * quotient(zeta)`; `OodEvaluationMismatch` otherwise.
## 2. Ground-truth extractor (real p3-uni-stark proofs)
`pqc-fork/pq-stark/airquotient-extractor/` (Cargo.toml + Cargo.lock + src/main.rs), depending on pinned
`p3-uni-stark` / `p3-air` / `p3-baby-bear` / `p3-commit` 0.4.3-succinct (checksums
`fc3dfdeb...` / `d3a5de20...` / `d69e6e9a...` / `50acacc7...`; the last two match the Poseidon2/MMCS
components). Built + run in a scratch target dir (never in the repo target); deterministic (regenerated
byte-identical). For two KNOWN example AIRs it runs `p3_uni_stark::prove` then `p3_uni_stark::verify`,
asserts the library ACCEPTS, and emits degree_bits / quotient_degree / trace openings / quotient-chunk
openings / alpha / zeta (alpha, zeta re-derived by replaying the verifier's exact transcript prefix; the
opened_values / commitments are recovered from the proof's own serde serialization):
- `fibonacci_n8`: Fibonacci AIR verbatim from `p3-uni-stark`'s own `tests/fib_air.rs` (n=8, pis=[0,1,21]),
1 quotient chunk (log_quotient_degree=0).
- `mul_deg3_n16`: degree-3 multiply AIR matching `tests/mul_air.rs` (a^2*b - c, boundary b=a^2+1,
transition next_a=a+1; n=16), 2 quotient chunks (log_quotient_degree=1) => exercises the zps product.
Output: `pqc-fork/pq-stark/air_quotient_ground_truth.json`.
## 3. Generic reference + KAT (RAN, PASS)
Three independent implementations of the generic identity: `air_quotient_reference.py`,
`air_quotient_reference.mjs`, `AirQuotientSelfTest.java`. Harness `test_air_quotient.py`:
- Accept: the generic identity holds for both genuine proofs (matches the library ACCEPT).
- Reject: corrupt a trace opening -> identity fails; wrong quotient chunk -> fails; wrong alpha -> fails.
- Cross-language: Python/Node/Java produce byte-identical reconstructed quotient(zeta) and
folded_constraints(zeta).
- Sanity: extractor perm_zeros == confirmed Poseidon2 "zeros" vector; val_generator == 31.
RESULT: PASS=18 FAIL=0. Recorded in `pqc-fork/results/kat-results-air-quotient.json`.
## 4. Wired into the precompile, GATED, fail-closed (RAN, PASS)
`StarkConstraints` in `Sp1StarkVerifierPrecompiledContract.java`:
- `GENERIC_QUOTIENT_CHECK_CONFIRMED = true`, `checkGenericQuotient(...)` implements the identity over
BabyBearExt4 (component (a)); exercised only for a SUPPLIED example AIR under the KAT.
- `SP1_RECURSION_AIR_PORTED = false` (THE gate). `evaluateAtZeta` (real path) returns UNAVAILABLE while it
is false; driver checks it at stage 4 before the query loop and before the single `return ACCEPT`.
- Runtime check `PrecompileAirCheck` (drives the REAL precompile classes, compiled against minimal Besu
stubs): PASS=12 FAIL=0. Confirms the generic check accepts the ground truth + rejects tampers,
`evaluateAtZeta(null,null) == UNAVAILABLE`, gate flags correct, and `computePrecompile` on a well-formed
AS1 wire input returns EMPTY (ACCEPT unreachable for a real SP1 proof).
## 5. What remains (honest, out of scope here)
The SP1-recursion-SPECIFIC AIR + vkey binding (spec section 6.2): the entire `sp1-recursion-core` /
`sp1-stark` symbolic constraint system (many columns, selectors, interaction/permutation argument,
public-value bindings) and the vkey digest that commits to it. Plus a real end-to-end SP1-proof KAT (needs
the SP1 toolchain + an exported SP1 v6.1.0 inner proof + the WireReader proof-body parser). Effort ~3 to 4
person-weeks + specialist STARK review. Marked [MEASURE].
The honest bottom line: the GENERIC BabyBear STARK verifier skeleton is now complete and
conformance-confirmed on all 6 generic components (a-f), but verifying a real SP1 proof still needs the
SP1-recursion-specific AIR + vkey, a real end-to-end proof KAT, an external audit, and founder activation.
On-chain ZK verifiers on Aere remain classical BN254 Groth16 until the whole verifier is real and
founder-activated. DO NOT ACTIVATE.
## Files
- New: `pqc-fork/pq-stark/air_quotient_reference.py` / `.mjs`, `AirQuotientSelfTest.java`,
`test_air_quotient.py`, `air_quotient_ground_truth.json`, `airquotient-extractor/` (Cargo.toml +
Cargo.lock + src/main.rs), `pqc-fork/results/kat-results-air-quotient.json`.
- Edited: `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java` (StarkConstraints: generic
mechanism + gate; class banner + stage-4 comment), `docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (status
header, section 6 rewritten into 6.1 generic + 6.2 SP1-specific, section 9 table, files),
`pqc-fork/pq-stark/README.md` (status bullets, new (e) section, layout table, reproduce commands).

View File

@ -0,0 +1,85 @@
# BabyBear field + F_{p^4}: component (a) of the STARK-verify port (0x0AE8)
> **Scope caveat (2026-07-19 finding).** This component is conformance-confirmed against Plonky3
> `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack, the `zk-circuits/*` provers), NOT against
> SP1 6.1.0. SP1 6.1.0 is a **Hypercube** release (KoalaBear multilinear: BaseFold + Jagged +
> sumcheck-zerocheck + LogUp-GKR), so FRI and DEEP-ALI do not apply to it and this skeleton does not
> verify SP1 6.1.0 proofs. Replacing the SP1 BN254 Groth16 wrap is a separate ~22 to 32 person-week
> retarget (a founder decision). See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Task: implement + validate the NEXT sub-component of the native STARK-verify port
(#2, precompile 0x0AE8): the BabyBear field F_p and its degree-4 extension F_{p^4}, the foundation
layer everything else in the verifier is built on.
## What was implemented
Matched the existing pq-stark sub-component convention (the prior FRI query-index piece is
Python + Node + standalone Java + a Python cross-language harness, with the production logic living
in the Java precompile). The SP1 guest side is Rust/Plonky3, but the port TARGET for 0x0AE8 is the
Java Besu precompile and the direct sibling sub-component is Python/Node/Java, so I matched that.
Production side (Java precompile, behind the still-fail-closed top level):
`aerenew/pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java`
- `BabyBear` (was: add/sub/mul/pow/inv): ADDED `neg`, `GENERATOR = 31`, `TWO_ADICITY = 27`,
`twoAdicGenerator(bits)`.
- `BabyBearExt4` (was: add/mul with W as `W_UNSET`/`TODO(port)`): PINNED `W = 11`, removed the
unset-guard, ADDED `sub`, `neg`, `fromBase` (base embedding), `pow(BigInteger)`, `inv` (Fermat
a^(p^4-2)), `frobenius` (a^p).
- Updated the honest-status javadoc to note the extension is now complete. The DO NOT ACTIVATE /
verifies-nothing banner and the whole fail-closed driver are untouched.
Independent references + tests (new files in `aerenew/pqc-fork/pq-stark/`):
- `babybear_field_reference.py` - Python reference (Fp + Fp4).
- `babybear_field_reference.mjs` - Node/BigInt reference (second language).
- `BabyBearFieldSelfTest.java` - standalone (no-Besu-classpath) mirror of the precompile field
logic, self-tested, with `--emit` for cross-language comparison.
- `test_babybear_field.py` - harness: axioms + Fermat-vs-bignum inverse + generator/W proofs +
Python/Node/Java agreement. Writes `../results/kat-results-babybear-field.json`.
## REAL test result (ran 2026-07-19)
- `python test_babybear_field.py` -> PASS=396242 FAIL=0, Python + Node + Java byte-identical.
- `java BabyBearFieldSelfTest` -> PASS=360018 FAIL=0.
- Precompile field edits are compile-clean: `javac` on the precompile shows only the expected
missing-Besu-symbol errors (the Besu jars are on the infra box, not local); zero errors fall in the
field-class line range 217-403, and the byte-identical standalone mirror compiles + runs.
Proven OFFLINE (no external fetch), all passing:
- F_p and F_{p^4} field axioms (comm/assoc/distrib/identities, add/sub/neg/mul inverses).
- Fermat inverse a^(p-2) == an INDEPENDENT extended-Euclid bignum inverse (Python `pow(a,-1,p)`).
- p = 2^31 - 2^27 + 1 = 2013265921; p-1 = 2^27 * 3 * 5 (2-adicity 27); p == 1 mod 4.
- `GENERATOR = 31` has multiplicative order exactly p-1 (via the known factorization).
- `twoAdicGenerator(k)` has order exactly 2^k for k = 1..27; twoAdicGenerator(27) = 440564289.
- `W = 11` is a quadratic non-residue and p == 1 mod 4, so x^4 - 11 is IRREDUCIBLE over F_p
(Lidl-Niederreiter binomial criterion), i.e. F_{p^4} = F_p[x]/(x^4 - 11) is a genuine field;
x^4 == W holds in the extension; ord(W) = 2^27 * 5, ord(x) = 4*ord(W) divides 4(p-1).
- Frobenius^4 = identity, Frobenius fixes the base field, base embedding is a ring homomorphism.
## Extension constants: confirmed vs [VERIFY]
- The field being REAL is PROVEN offline: 31 is a full-order generator, and x^4 - 11 is irreducible
so F_{p^4} is a genuine field.
- Whether GENERATOR = 31, W = 11, and twoAdicGenerator(27) = 440564289 are Plonky3
`p3-baby-bear`'s EXACT chosen constants is **[VERIFY]/[MEASURE]** against the pinned SP1 v6.1.0
Plonky3 revision. Could not confirm offline in this pass; marked, not faked. A
wrong-but-still-irreducible W would build a valid field that silently disagrees with the prover, so
irreducibility is necessary but not sufficient. (W = 11 is the value the design doc / skeleton
already cited as likely; I did not invent it.)
## Fail-closed confirmation
The top-level 0x0AE8 precompile still verifies NOTHING and stays fail-closed: `Poseidon2Bb.available`
is false, `StarkConstraints.evaluateAtZeta` and `Fri.checkQuery` still return UNAVAILABLE, and
`computePrecompile` returns EMPTY for every input. The completed field is a library dependency behind
the un-ported crypto core; it does not make the single `return ACCEPT` line reachable. On-chain ZK
verifiers remain the classical BN254 Groth16 gateway (Shor-breakable) until the whole STARK verifier
is real, audited, and founder-activated. This remains a multi-week port: components (b) Poseidon2,
(c) Merkle/MMCS, the rest of (d) FRI, (e) the AIR, and (f) the transcript are still un-ported.
## Docs updated
- `aerenew/docs/AERE-STARK-VERIFIER-PORT-SPEC.md`: section 2 (component (a) status -> IMPLEMENTED +
TESTED, with the remaining [VERIFY]/[MEASURE] items), the section 9 effort-table row (a), the
top-level status line (ONE -> TWO validated sub-components), and the section 10 file list.
- `aerenew/pqc-fork/pq-stark/README.md`: status bullets, layout table, reproduce commands, and a new
component-(a) section.

View File

@ -0,0 +1,78 @@
# Spec 2: PQ STARK-verify precompile 0x0AE8 core (port spec + one validated sub-component)
> **Scope caveat (2026-07-19 finding).** The port work here is conformance-confirmed against Plonky3
> `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1 6.1.0. SP1 6.1.0 is a
> **Hypercube** release (KoalaBear multilinear: BaseFold + Jagged + sumcheck-zerocheck + LogUp-GKR),
> so FRI and DEEP-ALI do not apply to it and this skeleton does not verify SP1 6.1.0 proofs. The
> ~11-16 person-week estimate below is the cost of a BabyBear+FRI verifier; replacing the SP1 BN254
> Groth16 wrap is a separate ~22 to 32 person-week retarget. See
> `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Task: advance the native PQ STARK-verify precompile 0x0AE8 honestly. The
top-level verifier stays a fail-closed skeleton that verifies NOTHING; the deliverable is a
detailed porting spec plus ONE genuinely-tractable, independently-tested sub-component.
## Deliverables
1. Porting spec: `aerenew/docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (new).
Component-by-component port plan, bottom-up dependency order:
(a) BabyBear field p = 2^31 - 2^27 + 1 and F_{p^4} ~0.5 wk, low risk (pin W)
(b) Poseidon2 over BabyBear (the hash) ~1.5-2 wk, error-prone constants
(c) Merkle / FRI vector commitment (MMCS) ~1-1.5 wk
(d) FRI folding + query-index derivation + LDT ~2-3 wk, hard (index slice done, see 2)
(e) AIR eval + SP1 recursion vk binding ~3-4 wk, HARDEST
(f) Fiat-Shamir transcript (Poseidon2 duplex) ~0.5-1 wk, pervasive
Each has: what it does, interface, the test vectors that validate it + their source, effort
estimate, hardest parts, and [VERIFY] flags. Total ~11-16 person-weeks (3-4 months + audit) for
one specialist. Stated plainly as a multi-week port; hardest = (e) recursion AIR, then (d) fold
arithmetic, then (b) Poseidon2 constants.
2. Implemented + tested sub-component: FRI query-index derivation (constant-free slice of (d)).
Chosen over Poseidon2 because it is fixed by public Plonky3 spec with NO secret round constants,
so it can be validated offline with running, PASSING tests. Poseidon2 conformance needs the exact
Plonky3 constants (offline it would only prove self-consistency, the "two wrong copies agree"
trap), so it is left as a fully-specified [VERIFY] port target in the spec.
Implements: `sampleBits(canonicalU32, bits)` (low-bits reduction of a challenger sample) and
`walk(index, logMaxHeight, logFinalPolyLen)` (per-layer folding-index walk: index, sibling=index^1,
index_pair=index>>1, parity; terminating in the final-poly domain).
Files:
- `aerenew/pqc-fork/pq-stark/fri_query_index_reference.py` (Python reference)
- `aerenew/pqc-fork/pq-stark/fri_query_index_reference.mjs` (Node, independent second language)
- `aerenew/pqc-fork/pq-stark/FriQueryIndexSelfTest.java` (standalone Java, no Besu classpath)
- `aerenew/pqc-fork/pq-stark/test_fri_query_index.py` (harness: goldens + invariants + alt-algo
+ cross-language agreement) -> `aerenew/pqc-fork/results/kat-results-fri-query-index.json`
- `FriQueryIndex` helper added to `Sp1StarkVerifierPrecompiledContract.java`, wired into
`Fri.checkQuery` (still returns UNAVAILABLE) behind the fail-closed top level.
## REAL test result (ran here)
- `python test_fri_query_index.py` -> PASS=100021 FAIL=0. Python + Node + Java produce byte-identical
results on the shared vector set (cross-language: java, node, python).
- `java FriQueryIndexSelfTest` -> PASS=80018 FAIL=0 (standalone, validates the exact Java helper that
ships in the precompile).
- Validates the index LOGIC vs the public Plonky3 p3-fri spec across three independent
implementations. Does NOT prove conformance to a real SP1 v6.1.0 trace -> that is [MEASURE]
(needs the Poseidon2 challenger + an exported proof; exact command in port spec section 8).
## Fail-closed confirmation
Top level unchanged: `StarkConstraints.evaluateAtZeta` still returns UNAVAILABLE, so `verify()`
returns UNAVAILABLE -> `computePrecompile` returns EMPTY for every input. Added a belt-and-braces
guard so an empty/short query-index array can never fall through to a vacuous ACCEPT. The Solidity
adapter still reverts on every proof. Honesty banners ("DO NOT ACTIVATE ON MAINNET / verifies
nothing yet") intact. On-chain ZK verifiers remain classical BN254 Groth16 (Shor-breakable), stated
plainly.
## Flags
- [VERIFY] sample_bits masks LOW bits (LSB) of as_canonical_u32 vs p3-challenger
- [VERIFY] arity-2 fold (sibling=index^1, index_pair=index>>1) vs p3-fri verifier.rs
- [VERIFY] logMaxHeight = log_max_degree + log_blowup; num_fold_rounds = logMaxHeight - logFinalPolyLen
- [VERIFY] BabyBear F_{p^4} non-residue W (skeleton cites x^4 - 11), Poseidon2 width 16 / 13 internal
rounds / M4 / diagonal / 141 round constants (all in the spec, NOT invented)
- [MEASURE] bit-reversal index-to-domain-point mapping vs a real Plonky3 trace
- [MEASURE] end-to-end derive_query_indices vs a real SP1 v6.1.0 inner proof
- [MEASURE] Poseidon2-BabyBear conformance vs p3-baby-bear KAT (whole permutation, port target)
Extra grounded fact (checked in the harness, not part of the FRI component): Poseidon2 S-box is x^7
because 7 is the smallest d>1 coprime to p-1 = 2^27 * 3 * 5.

View File

@ -0,0 +1,190 @@
# STARK-verifier doc-accuracy sweep: Hypercube / retarget caveat
Date: 2026-07-19. Scope: docs only. No contract, precompile, or code touched. The source-of-truth
finding lives in `aerenew/docs/AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md` (and the fuller
`AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md`), which were already correct and were NOT edited.
## The correct framing applied everywhere
Aere has a real, conformance-confirmed native BabyBear + FRI STARK verifier skeleton (all 6 generic
components validated byte-for-byte against the pinned Plonky3 `0.4.3-succinct` crates). It verifies
BabyBear + FRI STARKs, for example Aere's own Plonky3 zk-circuits. It does NOT verify SP1 6.1.0
proofs: the pinned SP1 (facade `= "=6.1.0"`, internals `sp1-hypercube 6.3.1`) is Hypercube (KoalaBear
multilinear: BaseFold + Jagged/Stacked PCS + sumcheck-zerocheck + LogUp-GKR with a septic-curve
digest), a different stack, so FRI and DEEP-ALI (the two biggest confirmed pieces) do not apply to it
at all. Replacing the BN254 Groth16 wrap on real SP1 proofs is a separate ~22 to 32 person-week
retarget (a founder decision). The real achievement is kept stated; only the scope is corrected.
## Files changed (20) and the overclaim fixed in each
### Explicit task files
1. `aerenew/docs/AERE-STARK-VERIFIER-PORT-SPEC.md`
- Overclaim: framed the whole ~11-16 person-week port as building a verifier for "an SP1 v6.1.0
INNER hash-based STARK proof: a Plonky3 ShardProof over the BabyBearPoseidon2 config" that
"removes the BN254 Groth16 wrap ... from the SP1 trust chain"; called 0x0AE8 "the post-quantum
replacement being built."
- Fix: prominent SCOPE CAVEAT block at the very top; corrected Section 0 non-claims bullet;
corrected the Section 1 target paragraph; added a scoped conclusion after Section 9 (the effort
table is the cost of a BabyBear+FRI verifier, not an SP1 6.1.0 verifier).
2. `aerenew/docs/AERE-BUILD-LOOP-SUMMARY-2026-07-19.md`
- Overclaim risk: STARK row (item 2) could read as progress toward verifying SP1 proofs.
- Fix: expanded the row with the scope caveat; explicitly states "does NOT let us verify SP1 proofs
now," six generic components verify BabyBear+FRI (Aere's own circuits), retarget is separate.
3. Per-component summaries (one-line scope caveat added at top of each; not rewritten wholesale):
- `AERE-STARK-BABYBEAR-FIELD-SUMMARY.md`
- `AERE-STARK-POSEIDON2-SUMMARY.md`
- `AERE-STARK-POSEIDON2-CONSTANTS-CONFIRMED.md`
- `AERE-STARK-MMCS-SUMMARY.md`
- `AERE-STARK-FRI-SUMMARY.md` (notes SP1 6.1.0 uses BaseFold, not FRI)
- `AERE-STARK-TRANSCRIPT-SUMMARY.md`
- `AERE-STARK-AIR-QUOTIENT-SUMMARY.md` (notes the "six generic components confirmed" claim means a
BabyBear+FRI skeleton, NOT an SP1 6.1.0 verifier; sumcheck-zerocheck not DEEP-ALI in SP1 6.1.0)
- `AERE-STARK-CORE-ADVANCE-SUMMARY.md`
- Overclaim in each: framed as a component of the "SP1/Plonky3 inner STARK-verify port."
4. `aerenew/pqc-fork/pq-stark/README.md`
- Overclaim: title + opening claimed it "verifies SP1's INNER hash-based Plonky3/FRI STARK
directly ... removes the one quantum-vulnerable link in the SP1 proof chain."
- Fix: prominent SCOPE CAVEAT block at top; retitled to "BabyBear/Plonky3 FRI/STARK verify
skeleton"; rescoped the opening paragraph to Aere's own Plonky3 circuits, not SP1 6.1.0.
### Sweep: docs tree
5. `aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md` (Section 1.3 ZK-verification, quantum bullet)
- Overclaim: "direct hash-based STARK verifier (verifying SP1's inner Plonky3 / FRI proof ...)".
- Fix: replaced the parenthetical with the scope caveat (BabyBear+FRI targets Aere's own circuits;
SP1 6.1.0 is Hypercube; removing the SP1 Groth16 wrap is a separate founder-gated retarget).
- Note: A-9 ("BN254 is honest until replaced") and the guarantees table correctly state on-chain
ZK verifiers are classical BN254 today; left TRUE and unchanged (points to the corrected 1.3).
6. `aerenew/docs/AERE-BENCHMARK-REPORT.md` (Section B.3)
- Overclaim: "a native precompile at 0x0AE8 that verifies SP1's INNER Plonky3 / FRI STARK
directly."
- Fix: rescoped to "a BabyBear / Plonky3 FRI STARK"; added a dedicated scope-caveat paragraph and
adjusted the honest-status paragraph ("not a working verifier for SP1 proofs"; SP1 target is a
different Hypercube system; retarget is separate).
7. `aerenew/docs/PQ-STARK-VERIFIER-PRECOMPILE-2026-07-18.md` (design + spec, dated pre-finding)
- Overclaim: whole premise, incl. title "direct SP1/Plonky3 inner FRI/STARK verification" and
"The precompile verifies an SP1 v6.1.0 inner STARK proof - a Plonky3 ShardProof over the
BabyBearPoseidon2 config."
- Fix: prominent UPDATE / SCOPE CORRECTION banner at top stating the premise is incorrect for the
pinned SP1 (Hypercube), directing readers to read "SP1 inner STARK / ShardProof" as "a BabyBear +
FRI STARK, i.e. Aere's own Plonky3 circuits." Body left as the historical design record.
8. `aerenew/docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md`
- Overclaim: P0 gate assumes activating a verifier of "a real SP1 v6.1.0 inner-STARK."
- Fix: UPDATE / SCOPE CORRECTION banner at top (skeleton verifies Aere's own Plonky3 STARKs, not
SP1 6.1.0; a founder-gated Hypercube retarget must precede any SP1-proof activation).
9. `aerenew/docs/AERE-AIP-PROCESS-AND-INDEX.md` (AIP-16)
- Overclaim: Decision said 0x0AE8 "verifies an SP1 inner-STARK (FRI over a Poseidon2 commitment)."
- Fix: corrected the Decision to "verifies a hash-based STARK directly"; added a scope-caveat
paragraph immediately below (BabyBear+FRI targets Aere's own circuits; SP1 6.1.0 is Hypercube;
retarget is separate).
10. `aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md` (0x0AE8 row)
- Overclaim: name column "SP1 inner STARK verify."
- Fix: relabeled "BabyBear/FRI STARK verify (skeleton)"; added the scope caveat to the notes cell.
11. `aerenew/docs/AERE-PROTOCOL-SPECIFICATION.md` (Section 4.8.3, precompile table, intro list, recap)
- Overclaim: 4.8.3 "direct on-chain verification of an SP1 (Plonky3) inner hash-based FRI/STARK
proof ... remove the only quantum-vulnerable link (the BN254 Groth16 outer wrap) from the SP1
proof chain"; table row "SP1 inner STARK verify | Plonky3 FRI/STARK"; two label mentions
(Section 4.6 list at line ~45 and the non-live recap at line ~670).
- Fix: retitled 4.8.3 to "hash-based STARK verify," added the scope caveat to its body; relabeled
the table row and both list mentions to "hash-based STARK verify (BabyBear/FRI, targets Aere's
own Plonky3 circuits, NOT SP1 6.1.0 which is Hypercube; see 4.8.3)."
12. `aerenew/docs/AERE-PQ-FINALITY-CERTIFICATE.md`
- Overclaim: "SP1's core proof system is a FRI-based STARK" (factually wrong for SP1 6.1.0), and
tying the AerePQStarkVerifier line to keeping SP1 proof verification quantum-safe.
- Fix: corrected the clause (hash-based hence Shor-resistant reasoning kept; "FRI" label
corrected: SP1 6.1.0 is Hypercube/BaseFold), and added that the 0x0AE8 BabyBear+FRI skeleton
verifies Aere's OWN Plonky3 aggregation STARK, not an SP1 6.1.0 proof.
13. `aerenew/docs/AERE-XMSS-AGGREGATION-CIRCUIT.md`
- Overclaim: native-STARK-vs-Groth16 tradeoff implied the AerePQStarkVerifier line verifies the
SP1-zkVM aggregation proof.
- Fix: appended a scope caveat (AerePQStarkVerifier is BabyBear+FRI for Aere's own STARKs; the
native-STARK option means an Aere-native BabyBear/FRI proof or a Hypercube retarget, not
verifying an SP1 6.1.0 zkVM proof directly).
14. `aerenew/docs/AERE-QUANTUM-SAFE-NETWORKING.md` (layer-3 quantum-layer map)
- Overclaim: cell "PQ STARK verify, off BN254 | building" implied an imminent SP1-path
replacement.
- Fix: annotated the cell ("scope-corrected") and added a note under the table (skeleton is
BabyBear+FRI for Aere's own circuits, does NOT yet verify SP1 6.1.0, retarget is separate,
on-chain zk verifiers remain classical BN254 today).
15. `aerenew/docs/AERE-ARCHITECTURE-MAP.md` (cross-cutting ZK-verification section)
- Was already honest ("A post-quantum STARK verifier is roadmap"; on-chain verifiers classical
BN254 today). Added a scope caveat for precision (0x0AE8 skeleton is BabyBear+FRI for Aere's own
circuits; does NOT yet verify SP1 6.1.0; retarget separate; "not done or imminent"). The
BN254-today statement was TRUE and left unchanged.
16. `aerenew/docs/AERE-QA-CONSOLIDATION-REPORT.md` (precompile-consistency item 2)
- The consistency snapshot propagated the "SP1 inner STARK verify" label.
- Fix: relabeled 0x0AE8 to "hash-based STARK verify" and appended the scope caveat.
### Sweep: pqc-fork tree
17. `aerenew/pqc-fork/pq-stark/spec-mmcs-babybear.md`
- Overclaim: "the exact SP1 inner config" / "which MMCS Plonky3/SP1 use."
- Fix: top scope caveat ("Plonky3/SP1" means Plonky3 `0.4.3-succinct`, Aere's own BabyBear+FRI
stack, not SP1 6.1.0 Hypercube; confirmed for a BabyBear+FRI verifier, not an SP1 6.1.0 one).
18. `aerenew/pqc-fork/pq-stark/spec-fri-babybear.md`
- Overclaim: "the exact FRI low-degree test Plonky3/SP1 use."
- Fix: top scope caveat (SP1 6.1.0's inner recursion uses BaseFold not FRI; FRI does not apply to
SP1 6.1.0; confirmed for Aere's own Plonky3 circuits).
19. `aerenew/pqc-fork/pq-stark/spec-poseidon2-constants.md`
- Overclaim: constants framed as those "Plonky3/SP1 use."
- Fix: top scope caveat (these are BabyBear constants; SP1 6.1.0 is KoalaBear/Hypercube and uses
different constants; confirmed for a BabyBear+FRI verifier).
20. `aerenew/pqc-fork/pq-stark/export-inner-stark-vector.md`
- Overclaim: whole premise "export the SP1 inner hash-based FRI/STARK proof (the shrink/compress
Plonky3 shard proof over BabyBear + Poseidon2 + FRI) from the pinned SP1 v6.1.0 toolchain."
- Fix: prominent SCOPE CORRECTION banner (SP1 6.1.0 exports a Hypercube/KoalaBear ShardProof, not
a BabyBear+FRI ShardProof, which this skeleton does not verify; a real corpus for the current
skeleton must come from Aere's own Plonky3 BabyBear+FRI provers; an SP1 6.1.0 corpus needs the
Hypercube retarget first). Rescoped the opening paragraph accordingly.
## Files reviewed, NO change needed (no overclaim found)
- `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`, `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md`:
source of truth; already document the Hypercube finding and the ~22-32 wk retarget correctly.
- `WHITEPAPER-V2.md`, `wp2-sections/02-architecture.md`: their "verifies SP1 ... proofs" references
are to the LIVE BN254 SP1 Groth16 gateway (`0x9ca479...`) via SP1VerifierGateway. Correct and TRUE;
no native-STARK-verifies-SP1 claim. No change.
- `wp2-sections/04-zk.md`, `06-mev-interop.md`, `07-compliance.md`: no 0x0AE8 / native-STARK-replaces-
SP1 claim (only live-BN254 references). No change.
- `AERE-PQ-FINALITY-CERTIFICATE-SUMMARY.md` (line ~53): "native hash-based STARK proof" is generic and
honestly marked [VERIFY]/design-only; hash-based remains true for Hypercube; no FRI/SP1-verification
overclaim. No change (parent certificate doc already caveated).
- `AERE-MIGRATION-TOOLKIT.md`, `AERE-NIST-VALIDATION-STATUS.md`: only mention 0x0AE8 as testnet/non-
live; no verification-capability claim. No change.
- `PQ-CONSENSUS-LIVE-READINESS-2026-07-18.md`, `ZK-PQ-FINALITY-2026-07-18.md`,
`ZK-ANCHOR-FIXES-2026-07-17.md`, `PROVEN-RESULTS-2026-07-11.md`, `FRONTIER-PROGRAM-2026-07-12.md`,
`VALIDITY-ROLLUP-SEQUENCER-PLAN.md`, `AUDIT-REPORT-2026-07-14.md`: matched only the broad initial
grep via live-BN254 SP1 gateway references; no native-STARK-verifies-SP1 / BN254-replacement claim.
No change.
## Guardrails honored
- Docs only. No contract, precompile (0x0AE8 stays fail-closed), or other code touched.
- Real achievement kept stated accurately (BabyBear+FRI skeleton is real and conformance-confirmed);
only scope corrected. No overclaim in the other direction.
- No em-dashes and no prose "--" introduced (verified; pre-existing em-dashes elsewhere left as-is).
- Brand: "Aere Network" / "Aere" title case; token TICKER "AERE" uppercase; addresses/URLs untouched.
## Confirmation
After the sweep, no doc implies the 0x0AE8 STARK verifier verifies SP1 6.1.0 proofs, replaces the
BN254 Groth16 gateway for SP1, or is a "native PQ STARK verifier for SP1," without the Hypercube /
retarget caveat. The on-chain ZK verifiers are still correctly stated as classical BN254 today.

76
AERE-STARK-FRI-SUMMARY.md Normal file
View File

@ -0,0 +1,76 @@
# Component (d) FRI verifier for Aere STARK-verify port (0x0AE8): summary
> **Scope caveat (2026-07-19 finding).** This FRI verifier is conformance-confirmed against Plonky3
> `p3-fri` `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1 6.1.0. SP1 6.1.0
> is a **Hypercube** release (KoalaBear multilinear), and its inner recursion proof uses **BaseFold,
> not FRI**, so this FRI low-degree test (one of the two largest confirmed pieces) does NOT apply to
> SP1 6.1.0 at all and this work does not verify SP1 6.1.0 proofs. Replacing the SP1 BN254 Groth16
> wrap is a separate ~22 to 32 person-week retarget. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Outcome: the FRI FOLD + OPENING relation (verify_query, the low-degree test) is
IMPLEMENTED and CONFORMANCE-CONFIRMED against the pinned Plonky3 p3-fri 0.4.3-succinct via a real KAT
(accept genuine + reject tampered). The top-level precompile 0x0AE8 STAYS FAIL-CLOSED.
## 1. Confirmed FRI config (traced from pinned p3-fri 0.4.3-succinct source)
Read from the cargo cache of `p3-fri` (checksum 5cbc4965..., matches repo Cargo.lock):
- Folding arity 2 (hard-coded): sibling = index ^ 1, index_pair = index >> 1 (verifier.rs verify_query).
- FriConfig fields: { log_blowup, num_queries, proof_of_work_bits, mmcs } (config.rs).
- log_max_height = commit_phase_commits.len() + log_blowup; num_fold_rounds = log_max_height - log_blowup.
- Final polynomial is a SINGLE F_{p^4} CONSTANT (final_poly: F); verify_challenges checks folded == final
for every query. (log_final_poly_len = 0 in this pinned version.)
- Commit-phase MMCS = ExtensionMmcs<Val, Challenge=BinomialExtensionField<BabyBear,4>, ValMmcs> over the
CONFIRMED FieldMerkleTreeMmcs (component (c)). A leaf is a PAIR of F_{p^4} evals flattened to 8 BabyBear
coords (ExtensionMmcs::verify_batch, as_base_slice per element), a width-8 row of height 2^log_folded_height.
- Grinding (proof_of_work_bits / check_witness) is in the transcript phase (verify_shape_and_sample_challenges),
i.e. component (f), NOT part of the per-query fold+opening relation.
- Fold: x = two_adic_generator(log_max_height)^reverse_bits_len(index, log_max_height) in F_{p^4}; sibling
point = x * two_adic_generator(1) = -x; folded = e0 + (beta - x0)*(e1 - e0)/(x1 - x0); plus
reduced_openings[log_folded_height+1] injected before each fold.
## 2. What I implemented
- Ground-truth extractor `pqc-fork/pq-stark/fri-extractor/` (Rust, depends on pinned p3-fri/p3-challenger/
p3-dft/p3-commit/p3-merkle-tree/p3-poseidon2/... =0.4.3-succinct; lockfile checksums matched). Builds the
exact SP1 inner FRI config with the seed_from_u64(1) Poseidon2, constructs genuinely low-degree codewords,
runs p3_fri::prover::prove to emit real FriProofs, and runs p3_fri::verifier::{verify_shape_and_sample_
challenges, verify_challenges} to confirm the LIBRARY accepts. Emits fri_ground_truth.json (roots, betas,
query indices, reduced openings, per-query per-layer sibling values + MMCS proofs, final poly, all canonical
u32) plus perm_zeros + two_adic_generators sanity anchors. 3 cases (blowup 1/2, height 6/7/8, 4/5/6 queries).
- FRI verifier reference (verify_query / verify_challenges) in 3 languages, reusing the CONFIRMED sub-components
(F_{p^4} = component (a), MMCS verifyBatch = component (c), Poseidon2 = component (b)):
`fri_verify_reference.py`, `fri_verify_reference.mjs`, standalone `FriVerifySelfTest.java`.
- Wired into the fail-closed Java precompile: `Fri.verifyQuery` (+ `Fri.reverseBitsLen`, `Fri.foldRelationConfirmed
= true`) in Sp1StarkVerifierPrecompiledContract.java, using the existing BabyBearExt4 / Mmcs.verifyBatch /
BabyBear.twoAdicGenerator. `Fri.checkQuery` kept GATED (returns UNAVAILABLE).
- Harness `test_fri_verify.py` -> writes `../results/kat-results-fri-verify.json`. Spec `spec-fri-babybear.md`.
Updated port spec + pq-stark README.
## 3. Conformance KAT result: PASSED (accept + reject)
- `python test_fri_verify.py` -> PASS=55 FAIL=0: sanity (perm_zeros == confirmed Poseidon2 zeros; 28 two-adic
generators == component (a)); 3 cases each ACCEPT the genuine p3-fri proof and REJECT four tampers (corrupted
opened value, corrupted MMCS sibling digest, wrong beta/fold, non-matching final poly); Python + Node + Java
produce BYTE-IDENTICAL per-query folded values.
- `java FriVerifySelfTest fri_ground_truth.json` -> PASS=44 FAIL=0.
- The PRECOMPILE's own `Sp1StarkVerifierPrecompiledContract.Fri.verifyQuery`, driven directly against the ground
truth, reproduces accept + reject for all 3 cases (15/15). So the wired code, not just parallel references, is
conformant (no "two wrong copies agree").
Closed by this KAT (were [VERIFY]): arity-2 fold; num_fold_rounds = log_max_height - log_blowup + constant final
poly; reverse_bits_len coset mapping; two_adic_generator(bits) == Plonky3's; F_{p^4} non-residue W = 11 == Plonky3's.
## 4. Top level stays FAIL-CLOSED (verified)
- Challenger.spongePorted = false (component (f) un-ported) and StarkConstraints.evaluateAtZeta returns
UNAVAILABLE (component (e) un-ported): verify() returns UNAVAILABLE at stage 4, never reaches ACCEPT.
- computePrecompile returns VERIFIED only on ACCEPT, else Bytes.EMPTY -> EMPTY for every input.
- The FRI fold+opening takes betas/indices/reduced-openings as INPUTS; deriving them trustlessly is component
(f) (the duplex-sponge challenger), so Fri.checkQuery stays gated (UNAVAILABLE). WireReader also does not parse
the commit-phase roots/openings. DO NOT ACTIVATE banner intact. Precompile compiles clean (Besu stubs, javac exit 0).
## 5. Scope boundary (unchanged)
On-chain ZK verifiers on Aere remain classical BN254 Groth16 until the whole verifier is real, audited, and
founder-activated. This is one more component of a multi-week port. Rust compiled only in a scratch/temp dir; no
live infra touched.

View File

@ -0,0 +1,97 @@
# STARK-verify port 0x0AE8, component (c): MMCS (FieldMerkleTreeMmcs) - IMPLEMENTED + CONFORMANCE-CONFIRMED
> **Scope caveat (2026-07-19 finding).** This component is conformance-confirmed against Plonky3
> `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1 6.1.0. SP1 6.1.0 is a
> **Hypercube** release (KoalaBear multilinear: BaseFold + Jagged + sumcheck-zerocheck + LogUp-GKR),
> so FRI and DEEP-ALI do not apply to it and this skeleton does not verify SP1 6.1.0 proofs. Replacing
> the SP1 BN254 Groth16 wrap is a separate ~22 to 32 person-week retarget. See
> `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Status: real known-answer test PASSES. Top-level precompile stays fail-closed.
## What was done
Implemented and conformance-confirmed component (c) of the SP1/Plonky3 inner STARK-verify port: the
Mixed Matrix Commitment Scheme (MMCS), i.e. Plonky3's `FieldMerkleTreeMmcs`, the Merkle-tree vector
commitment that FRI (d) and the trace commitment are built on. Same rigorous ground-truth method that
worked for Poseidon2: compiled and executed the exact pinned Plonky3 crates to emit authoritative
reference vectors, then reproduced them byte-for-byte.
### 1. Confirmed the exact construction from the pinned crates
Read the source of the pinned `0.4.3-succinct` crates (checksums matched the repo Cargo.lock):
`p3-merkle-tree` (d5703d92...), `p3-symmetric` (9047ce85...), `p3-commit` (50acacc7...),
`p3-matrix` (75c3f150...). The SP1 inner config is pinned VERBATIM in `p3-merkle-tree`'s own
`mmcs.rs` tests:
- leaf hasher = `PaddingFreeSponge<Perm, WIDTH=16, RATE=8, OUT=8>` (overwrite-mode, padding-free)
- 2-to-1 compr. = `TruncatedPermutation<Perm, N=2, CHUNK=8, WIDTH=16>` (permute(l||r)[0..8])
- MMCS = `FieldMerkleTreeMmcs<Packing, Packing, MyHash, MyCompress, DIGEST_ELEMS=8>`
So a digest = 8 BabyBear elements (32 bytes). Leaf hash / node compression run on the CONFIRMED
Poseidon2-BabyBear permutation (component (b)). Traced the tree build (`first_digest_layer` /
`compress_and_inject`), `open_batch`, and `verify_batch` from source; documented the mixed-height rule
(tallest-first, power-of-two padding with the zero digest, injection at the matching padded layer,
index reduced by `index >> (log_max_height - log2_ceil(height))`) and the height property that makes
the tree-build and verify grouping select the same set.
### 2. Ground-truth extractor (Rust, scratch dir only)
Wrote `mmcs-extractor` depending on the pinned crates, built the exact `MyMmcs`, and used the SAME
deterministic permutation as the Poseidon2 confirmation (`new_from_rng_128(seed_from_u64(1))`). It
commits to six known matrix batches and emits the root + an `open_batch` opening (opened rows + sibling
path) for each, printing every field element as canonical u32, and asserts the library's OWN
`verify_batch` accepts each. Its `permute([0;16])` equals the confirmed Poseidon2 "zeros" vector, so
the extractor's permutation is byte-identical to the reference's (guards the "two wrong copies" trap).
Ran on this box; all six library `verify_batch` calls returned Ok.
The six cases exercise: single power-of-two height (8x2), single non-power-of-two height (6x2, padded,
zero-digest padding), a column vector (8x1), and three mixed-height batches (8x2+4x3, 8x1+4x2+2x2,
5x2+3x1). One case (mixed_5x2_3x1) has a default (all-zero) sibling digest in its proof.
### 3. References + precompile wiring (matching the existing convention)
- `pqc-fork/pq-stark/mmcs_babybear_reference.py` (Python), `.mjs` (Node), `MmcsBabyBearSelfTest.java`
(standalone Java): hash_iter (PaddingFreeSponge), compress2to1 (TruncatedPermutation), full
mixed-height tree build + commit + open_batch + verify_batch, over the confirmed Poseidon2.
- `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java`: new `Mmcs` static class
(`hashIter`, `compress2to1`, mixed-height `verifyBatch`) wired behind the still-fail-closed top level;
`Mmcs.available = true`. Referenced from `Fri.checkQuery` (still returns UNAVAILABLE). Compiles clean.
- `pqc-fork/pq-stark/test_mmcs_babybear.py` harness + `../results/kat-results-mmcs-babybear.json`.
- `pqc-fork/pq-stark/spec-mmcs-babybear.md` provenance doc.
- Updated `docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (section 4 -> CONFIRMED, status table, intro, files)
and `pqc-fork/pq-stark/README.md` (new (c) section, layout, reproduce).
### 4. CONFORMANCE KAT: PASSED
`python test_mmcs_babybear.py` -> **PASS=300 FAIL=0**. CONFORMANCE 37/37 (perm sanity + 6 primitive
hash/compress KATs + 6 MMCS cases: root + openings + proof + verify-accept + tamper-reject) reproduced
EXACTLY from the pinned crates. Cross-language byte-identical across Python + Node + Java. Standalone
`java MmcsBabyBearSelfTest` -> PASS=80 FAIL=0 (includes open-every-valid-leaf self-consistency).
Poseidon2 regression (`test_poseidon2_babybear.py`) still PASS. No em-dashes / `--` in prose docs.
### Multi-matrix generalization
Handled beyond the single-matrix case: three genuine mixed-height batches (2 and 3 matrices) are in the
KAT and pass, so the tallest-first grouping, power-of-two padding, and injection logic are exercised,
not just documented.
## Fail-closed confirmation (unchanged)
The top-level 0x0AE8 still verifies NOTHING and returns EMPTY for every input:
- `StarkConstraints.evaluateAtZeta` returns UNAVAILABLE (stage 4 -> UNAVAILABLE -> EMPTY).
- `Challenger.spongePorted = false` (absorb no-op; `checkProofOfWork` false; `sampleQueryIndices` empty).
- `Fri.checkQuery` returns UNAVAILABLE.
- `computePrecompile` returns `VERIFIED` only if `verify(...) == ACCEPT`; the single `return ACCEPT`
line is unreachable. `Mmcs.available = true` flips NO top-level gate.
- DO NOT ACTIVATE banner intact.
## Honesty caveat ([MEASURE] remaining)
Conformance is against the pinned crate library (KAT reproduced byte-for-byte). Matching a REAL exported
SP1 v6.1.0 proof's actual commitment root (its specific trace / FRI matrices and heights) is a further
[MEASURE] step needing an exported proof + the challenger; flagged honestly, not faked.
## Scope boundary (unchanged)
On-chain ZK verifiers on Aere remain the classical BN254 Groth16 gateway until the whole STARK verifier
is real, audited, and founder-activated. This is one more component of a multi-week port. Consensus on
chain 2800 is unaffected (classical secp256k1 ECDSA QBFT).

View File

@ -0,0 +1,77 @@
# Poseidon2-BabyBear width-16 constants: confirmation + conformance (task summary)
> **Scope caveat (2026-07-19 finding).** These constants are confirmed against Plonky3
> `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1 6.1.0. SP1 6.1.0 is a
> **Hypercube** release over the **KoalaBear** field, so these BabyBear Poseidon2 constants are not
> SP1 6.1.0's constants, FRI and DEEP-ALI do not apply to SP1 6.1.0, and this work does not verify SP1
> 6.1.0 proofs. Replacing the SP1 BN254 Groth16 wrap is a separate ~22 to 32 person-week retarget.
> See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Component (b) of the PQ STARK-verify precompile 0x0AE8.
## Outcome (headline)
- Constants: **CONFIRMED-FROM-SOURCE.**
- Conformance KAT: **PASSED** (3/3 real known-answer vectors reproduced byte-for-byte).
- Top-level 0x0AE8: **still fail-closed** (returns EMPTY for every input). DO NOT ACTIVATE banner kept.
## What the prior pass had wrong, and what was confirmed
The prior pass implemented the permutation STRUCTURE but used SHA-256 PLACEHOLDER round constants
(`AERE-PLACEHOLDER-NOT-PLONKY3`) and a textbook internal layer. Two things were confirmed/fixed:
1. The 141 round constants are now the real Plonky3 values (128 external + 13 internal), generated by
`Xoroshiro128Plus::seed_from_u64(1)` via `new_from_rng_128` and extracted from the pinned crates.
2. The internal linear layer was WRONG (a genuine bug, the "two wrong copies agree" trap). The pinned
`DiffusionMatrixBabyBear`, as a canonical map, is `M_I = R^{-1} * (J + diag(D))` with
R^{-1} = 943718400 = (2^32)^{-1} mod p, i.e. `out[i] = R_INV*(sum + D[i]*state[i])`. The prior pass
used `state[i]*D[i] + sum` (no R^{-1}), which is self-consistent but disagrees with the prover.
This is the Montgomery-form subtlety the task flagged. Verified by recovering the exact 16x16
canonical matrix from the library (off-diagonal = R^{-1}, diagonal = R^{-1}*(1+D[i])).
Also confirmed: ROUNDS_F=8, ROUNDS_P=13 (`poseidon2_round_numbers_128(16,7)`); M4 =
[[2,3,1,1],[1,2,3,1],[1,1,2,3],[3,1,1,2]] (recovered from `Poseidon2ExternalMatrixGeneral`); the
internal diagonal D = [p-2,1,2,4,...,8192,32768] (canonical). The x^7 S-box was already solid.
## Source (exact) and method
Pinned target: `p3-baby-bear` / `p3-poseidon2` crates.io `0.4.3-succinct` (checksums
`d69e6e9a...` / `52298637...`, matching `aerenew/.../Cargo.lock`). The numbers are authoritative
because the pinned crates were compiled and executed (cargo 1.97.0; the fresh build resolved the same
two checksums, byte-identical to the AERE pin). Corroborating source pages:
- https://docs.rs/crate/p3-baby-bear/0.4.3-succinct/source/src/poseidon2.rs (diagonal, RNG comment)
- https://docs.rs/crate/p3-monty-31/0.4.3-succinct/source/src/monty_31.rs (Standard sampling, new_monty)
- https://docs.rs/crate/p3-poseidon2/0.4.3-succinct/source/src/lib.rs (new_from_rng_128 order)
- https://github.com/Plonky3/Plonky3 poseidon2/src/{round_numbers.rs, external.rs}
- https://github.com/rust-random/rngs rand_xoshiro (SplitMix64 seed, next_u32 = next_u64>>32)
Full provenance + the KAT vectors: `aerenew/pqc-fork/pq-stark/spec-poseidon2-constants.md`.
## Conformance KAT (executed from the pinned library, reproduced by all references)
- zeros [0;16] -> [1787823396, 953829438, 89382455, ...]
- iota [0..15] -> [157639285, 1851003038, 1852457045, ...]
- testvec (p3 test) -> [512585766, 975869435, 1921378527, ...]
Results: `python test_poseidon2_babybear.py` -> PASS=47017 FAIL=0 (Python/Node/Java byte-identical,
3/3 conformance KATs); `java Poseidon2BabyBearSelfTest` -> PASS=13536 FAIL=0.
## Files changed
- `aerenew/pqc-fork/pq-stark/poseidon2_babybear_reference.py` / `.mjs` — real constants, R_INV
internal layer, conformance KATs.
- `aerenew/pqc-fork/pq-stark/Poseidon2BabyBearSelfTest.java` — same; self-test PASS=13536.
- `aerenew/pqc-fork/pq-stark/test_poseidon2_babybear.py` — added conformance stage; honest status.
- `aerenew/pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java` — real constants, R_INV
internal layer, `Poseidon2Bb.available = true`; decoupled challenger gate
(`Challenger.spongePorted = false`) so the top level stays fail-closed.
- `aerenew/pqc-fork/pq-stark/README.md`, `aerenew/docs/AERE-STARK-VERIFIER-PORT-SPEC.md` — real status.
- New: `aerenew/pqc-fork/pq-stark/spec-poseidon2-constants.md` (provenance/citation doc).
## Fail-closed confirmation
`Poseidon2Bb.available = true` (permutation confirmed), but the duplex sponge is a separate un-ported
component: `Challenger.spongePorted = false` -> `checkProofOfWork` returns false and
`sampleQueryIndices` returns empty; `StarkConstraints.evaluateAtZeta` and `Fri.checkQuery` return
UNAVAILABLE. So `verify(...)` never reaches `return ACCEPT`; the precompile returns EMPTY for every
input. The DO NOT ACTIVATE banner is intact.

View File

@ -0,0 +1,94 @@
# Poseidon2-over-BabyBear (width 16) - component (b) of the 0x0AE8 STARK-verify port
> **Scope caveat (2026-07-19 finding).** This component is conformance-confirmed against Plonky3
> `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1 6.1.0. SP1 6.1.0 is a
> **Hypercube** release (KoalaBear multilinear: BaseFold + Jagged + sumcheck-zerocheck + LogUp-GKR),
> so FRI and DEEP-ALI do not apply to it and this skeleton does not verify SP1 6.1.0 proofs. (Only the
> Poseidon2 sponge SHAPE carries over to SP1's Hypercube stack, and even that needs the KoalaBear
> field and KoalaBear Poseidon2 constants.) Replacing the SP1 BN254 Groth16 wrap is a separate
> ~22 to 32 person-week retarget. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Status: STRUCTURE implemented + self-consistency TESTED; CONFORMANCE to Plonky3 is
[VERIFY] (no KAT run). Top-level 0x0AE8 verifier UNCHANGED and still fail-closed.
## What was implemented
The Poseidon2 permutation over BabyBear (p = 2^31 - 2^27 + 1) at the standard SP1/Plonky3 width 16:
- S-box x^7 (BabyBear: 7 is the smallest d>1 with gcd(d, p-1)=1, so it is a bijection on F_p).
- 8 external (full) rounds: 4 initial + 4 terminal, S-box on all 16 lanes, then the MDS-light
external layer M_E built from M4 = [[2,3,1,1],[1,2,3,1],[1,1,2,3],[3,1,1,2]] (block matrix with
diagonal blocks 2*M4, off-diagonal blocks M4; the two-step "apply M4 per block, add col sums" form).
- 13 internal (partial) rounds: S-box on lane 0 only, then M_I = J + diag(D) applied as
state[i] = state[i]*D[i] + sum(state), D = INTERNAL_DIAG_M1_16.
- Round-constant schedule: 128 external (add to all 16 lanes) + 13 internal (add to lane 0) = 141.
- A `compress2to1` truncated-permutation 2-to-1 compression for the Merkle layer (shape [VERIFY]).
Files (mirrors the field sub-component's Python + Node + standalone-Java + precompile convention):
- `aerenew/pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java` - `Poseidon2Bb.permute`
implemented; `Poseidon2Bb.available` STAYS false (gates everything above it).
- `aerenew/pqc-fork/pq-stark/poseidon2_babybear_reference.py` (Python reference).
- `aerenew/pqc-fork/pq-stark/poseidon2_babybear_reference.mjs` (Node reference).
- `aerenew/pqc-fork/pq-stark/Poseidon2BabyBearSelfTest.java` (standalone Java, no Besu classpath).
- `aerenew/pqc-fork/pq-stark/test_poseidon2_babybear.py` (harness).
- `aerenew/pqc-fork/results/kat-results-poseidon2-babybear.json` (machine-readable result).
- Docs updated: `aerenew/docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (intro, section 3, section 9 table,
section 10 files) and `aerenew/pqc-fork/pq-stark/README.md`.
## Self-consistency result (REAL, ran 2026-07-19)
`python test_poseidon2_babybear.py` -> PASS=47014 FAIL=0. Standalone `java Poseidon2BabyBearSelfTest`
-> PASS=13533 FAIL=0. Additionally the precompile's exact `Poseidon2Bb.permute` bytes were compiled
in a throwaway and matched the references on all 13 shared states.
Proven offline (genuine, and it IS self-consistency, not conformance):
- x^7 bijection: 7 CONFIRMED as the minimal coprime degree (p-1 = 2^27*3*5); monomial inverse
round-trips on 20k random x; S-box equals an independent bignum pow(x,7,p).
- Determinism + genuine BIJECTION: an explicit inverse permutation round-trips on the fixed set +
3000 random states; 0 output collisions on the sample.
- MDS / invertibility: M4 is MDS (all 69 square submatrices nonsingular); external and internal
layers equal their explicit 16x16 matrices; both matrices are invertible over F_p.
- Cross-language: Python, Node, and standalone Java produce BYTE-IDENTICAL permutation outputs on a
shared 13-state input set (all-zero, [0..15], all-(p-1), structured, and LCG-random states).
## Conformance = [VERIFY] (NOT tested; no KAT claimed)
This is the whole risk of Poseidon2 (the "two wrong copies agree" trap): a structurally-correct
permutation with the WRONG constants is self-consistent and cross-language identical yet silently
disagrees with the real prover. The constants here are NOT confirmed to be Plonky3's:
- The 141 round constants are DETERMINISTIC PLACEHOLDERS (SHA-256 of a labelled counter,
"AERE-PLACEHOLDER-NOT-PLONKY3|..."), cross-language reproducible but NOT Plonky3's. Plonky3
generates them from Xoroshiro128Plus::seed_from_u64(1) via new_from_rng_128.
- INTERNAL_DIAG_M1_16 = [p-2, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 32768]
is a CANDIDATE observed via a lossy docs.rs summary of p3-baby-bear's
POSEIDON2_INTERNAL_MATRIX_DIAG_16_BABYBEAR_MONTY. May be Montgomery-form; transcription unconfirmed;
not checksum-verified.
- ROUNDS_F=8, ROUNDS_P=13 are the commonly-cited counts; ROUNDS_P is [VERIFY]
(Plonky3 poseidon2_round_numbers_128(16, 7)).
- M4 and the MDS-light M_E construction are the Poseidon2-paper standard; byte identity vs
p3-poseidon2 is [VERIFY].
Pinned conformance target (found in the repo's Cargo.lock, not vendored as source): Plonky3
`p3-poseidon2` and `p3-baby-bear` at crates.io `0.4.3-succinct` (checksums
522986377b2164c5f94f2dae88e0e0a3d169cc6239202ef4aeb4322d60feffd0 and
d69e6e9af4eaaaa60f7bb9f0e0f73ebcbaefe7e00974d97ad0fa542d6a4f0890).
[VERIFY: confirm the p3-poseidon2 BabyBear width-16 round constants + internal diagonal + round
counts against SP1 v6.1.0 (Plonky3 0.4.3-succinct), regenerate the round constants from the pinned
RNG, replace the placeholders, then run the real KAT (all-zero state, [0,1,...,15]) against
p3-baby-bear's test_poseidon2_babybear output and report PASS/FAIL.]
## Top-level stays fail-closed
`Poseidon2Bb.available = false` (constants are [VERIFY], so it is NOT flipped). With it false, the
Challenger PoW check returns false, sampleQueryIndices returns empty, StarkConstraints.evaluateAtZeta
returns UNAVAILABLE, and Fri.checkQuery returns UNAVAILABLE. `Sp1StarkVerifierPrecompiledContract.verify`
therefore returns UNAVAILABLE and `computePrecompile` returns EMPTY (0x) for every input. The single
`return VerifyResult.ACCEPT` line is unreachable. The "DO NOT ACTIVATE ON MAINNET / verifies nothing"
banner is unchanged. On-chain ZK verifiers remain the classical BN254 Groth16 gateway. The multi-week
port continues: components (c) Merkle/MMCS, the rest of (d) FRI, (e) AIR, (f) transcript are un-ported,
and Poseidon2's own conformance is the flagged open item.
Sub-component tally: 3 of 6 components now have implemented+tested pieces (a field COMPLETE, b
Poseidon2 structure with [VERIFY] conformance, one constant-free slice of d). None makes 0x0AE8 accept.

View File

@ -0,0 +1,65 @@
# SP1-recursion AIR port spec: research summary
Deliverable written: `aerenew/docs/AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md` (planning doc, NOT an
implementation). Top-level 0x0AE8 untouched, stays fail-closed.
## Pinned version
- SP1 facade crates `= "=6.1.0"` (rollup-evm-validity/*, batch-prover-recovered/*).
- Upstream tag `v6.1.0`, commit `d454975ac7c1126097e36eceda9bce2cb9899da4`.
- Repo's OWN resolved lock (rollup-evm-validity/host/Cargo.lock): SP1 6.1.0 facade sits on
`sp1-hypercube 6.3.1`, `sp1-recursion-machine 6.3.1`, `sp1-recursion-executor 6.3.1`,
`slop-basefold/jagged/multilinear/sumcheck 6.3.1`.
## HEADLINE FINDING (correctness)
SP1 6.1.0 is **Hypercube, not Turbo**. Its inner recursion proof is NOT a BabyBear FRI
ShardProof. It is a KoalaBear multilinear proof: BaseFold + Stacked + Jagged PCS, sumcheck
zerocheck (not DEEP-ALI), LogUp-GKR interaction with a degree-7 septic-curve global digest.
- SP1Field = KoalaBear (0x7F000001), ext degree 4, Poseidon2 width 16 / 30 rounds
(crates/primitives/src/lib.rs @ v6.1.0).
- The generic skeleton (BabyBear + FRI + p3-uni-stark DEEP-ALI, Plonky3 0.4.3-succinct) was
confirmed against the WRONG stack for SP1 6.1.0. Only Poseidon2/Merkle/challenger SHAPES carry
over (need KoalaBear re-field + new constants); FRI and DEEP-ALI do not apply at all.
- The 0.4.3-succinct p3 crates in the repo lock are Aere's OWN Plonky3 circuits (zk-circuits/*),
not the SP1 recursion proof.
## Recursion AIR chip set (crates/recursion/machine/src/machine.rs @ v6.1.0)
Full: MemoryConst, MemoryVar, BaseAlu, ExtAlu, Poseidon2Wide, Poseidon2LinearLayer, Poseidon2SBox,
ExtFeltConvert, PrefixSumChecks, Select, PublicValues.
- compress_machine: 8 chips (Poseidon2Wide, no split, no ExtFeltConvert).
- wrap_machine: 9 chips (split Poseidon2 linear-layer + sbox, ExtFeltConvert, no PrefixSumChecks).
- NO FriFold / ExpReverseBits / BatchFRI (Turbo-only) -> confirms recursion no longer runs FRI.
## Interaction argument (biggest specific add)
LogUp-GKR (crates/hypercube/src/logup_gkr, lookup) + SepticDigest: elliptic curve over degree-7
"septic" extension of KoalaBear; offset-corrected cumulative sum (sqrt2/sqrt3 start points) for the
cross-chip/global multiset bus (crates/hypercube/src/septic_digest.rs @ v6.1.0).
## ShardProof shape (crates/hypercube/src/verifier/shard.rs @ v6.1.0)
main_commitment, opened_values (multilinear point, no univariate zeta), zerocheck_proof (sumcheck),
logup_gkr_proof, evaluation_proof (Jagged/BaseFold), public_values.
## vk + public values
- MachineVerifyingKey: preprocessed_commit, pc_start, initial_global_cumulative_sum (septic point),
enable_untrusted_programs. Digest = hash_koalabear() Poseidon2->[SP1Field;8]; hash_bn254() via
koalabears_to_bn254 (hashable_key.rs). Export doc's hash_babybear() is WRONG -> hash_koalabear().
- RecursionPublicValues (crates/recursion/executor/src/public_values.rs): committed_value_digest,
sp1_vk_digest, vk_root, global_cumulative_sum, is_complete, exit_code, pc_start/next_pc, timestamps,
digest, proof_nonce, etc. Count = RECURSIVE_PROOF_NUM_PV_ELTS ([VERIFY] exact int).
## Reference to follow
github.com/succinctlabs/hypercube-verifier (crates: basefold, jagged, stacked, sumcheck, tensor,
multilinear, merkle-tree, stark, recursion). Clean standalone verifier for exactly this system.
Caveat: 2025-05-20 research prototype; reconcile constants vs pinned 6.3.1 [VERIFY].
## Total realistic remaining
~22 to 32 person-weeks (~5 to 8 months) single specialist + external audit. Hardest: LogUp-GKR+septic,
BaseFold+Jagged PCS, sumcheck zerocheck, RecursionAir chips. Recommend wrapping a pinned native build
of the Rust reference over hand-porting into Java.
## [VERIFY]/[MEASURE] flags
version-skew pin (6.1.0 facade on 6.3.1 internals); per-chip constraint counts + DEGREE +
VAR_EVENTS_PER_ROW; RECURSIVE_PROOF_NUM_PV_ELTS int + PV order; BaseFold/Jagged params (blowup/arity/
queries/pow) for compress/shrink/wrap + whether CircuitWrapConfig differs; septic curve constants +
interaction-kind encoding + challenge order; hash_koalabear input list + limb packing; KoalaBear
Poseidon2 round split within 30 + constants; hypercube-verifier prototype vs 6.3.1 conformance;
end-to-end KAT needs a real exported proof (no offline substitute).

View File

@ -0,0 +1,474 @@
# AERE STARK verifier: SP1-recursion AIR port specification (precompile 0x0AE8, part 2)
Spec date: 2026-07-19
Status: RESEARCH + PORTING PLAN, not an implementation. This document scopes the ONE remaining
crypto-core piece of the native STARK-verify port (the SP1-recursion-specific constraint system and
its verifying key) against the REAL, pinned SP1 source. It does NOT port it. The top-level precompile
0x0AE8 stays fail-closed and is not touched by this document.
Companion: `docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (the six generic components + their confirmed
status), `docs/PQ-STARK-VERIFIER-PRECOMPILE-2026-07-18.md` (design/encoding/gas),
`docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md` (gated activation).
Citations use the pinned upstream tag `v6.1.0` (commit `d454975ac7c1126097e36eceda9bce2cb9899da4`,
github.com/succinctlabs/sp1). `[VERIFY]` marks a value or convention not read byte-exact from source in
this pass. `[MEASURE]` marks a validation that needs the compiled SP1 toolchain and a real proof.
---
## 0. Headline finding (read first): the pinned SP1 6.1.0 is Hypercube, not Turbo
This research turned up a correctness finding that changes the scope of the remaining work, so it goes
first and plainly.
The generic-component work in the companion spec was implemented and conformance-confirmed against
Plonky3 `0.4.3-succinct`: the BabyBear field, Poseidon2-over-BabyBear, a Merkle/MMCS, the FRI low-degree
test, a `p3-uni-stark` DEEP-ALI quotient check, and a `p3-challenger` duplex transcript. That is a
faithful BabyBear + FRI + univariate-STARK verifier skeleton, and it is real.
It is, however, the WRONG proof system for the version of SP1 that Aere actually pins. SP1 `=6.1.0`
(pinned in `rollup-evm-validity/host/Cargo.toml`, `rollup-evm-validity/guest/Cargo.toml`,
`batch-prover-recovered/bin/aere-prover/Cargo.toml`, all `sp1-sdk`/`sp1-prover`/`sp1-verifier`/`sp1-zkvm`
`= "=6.1.0"`) is an SP1 Hypercube release. Its proving stack is not AIR + FRI. It is a multilinear
system: KoalaBear field, a BaseFold + Stacked + Jagged polynomial commitment scheme, a sumcheck-based
constraint check (zerocheck), and a LogUp-GKR interaction argument with a degree-7 septic-curve global
cumulative digest. There is no BabyBear `ShardProof<BabyBearPoseidon2>` and no FRI low-degree test
anywhere in the SP1 6.1.0 proof that 0x0AE8 would verify.
Evidence, from both upstream source and the repo's own resolved lockfiles:
- Upstream `Cargo.toml` at tag v6.1.0 (`workspace.package.version = "6.1.0"`) lists workspace members
`slop/crates/{jagged, whir, basefold, sumcheck, multilinear, stacked, tensor, koala-bear, ...}` and
`sp1-gpu/crates/{jagged_sumcheck, logup_gkr, zerocheck, ...}`, and pins Plonky3 at `0.3.2-succinct`.
Source: https://github.com/succinctlabs/sp1/blob/v6.1.0/Cargo.toml
- `crates/primitives/src/lib.rs` defines `pub type SP1Field = KoalaBear;` and
`pub type SP1ExtensionField = BinomialExtensionField<SP1Field, 4>;` with `SP1GlobalContext =
KoalaBearDegree4Duplex` (Poseidon2 width 16, 30 total rounds).
Source: https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/primitives/src/lib.rs
- `crates/prover/src/recursion.rs` builds the compress/shrink/wrap recursion verifiers from
`RecursiveBasefoldVerifier` + `RecursiveMerkleTreeTcs` + `RecursiveStackedPcsVerifier` +
`RecursiveJaggedPcsVerifier` over `SP1Field`. There is no FRI verifier in that path.
Source: https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/prover/src/recursion.rs
- The repo's OWN resolved lock `rollup-evm-validity/host/Cargo.lock` (the crate that pins SP1 `=6.1.0`)
resolves `sp1-sdk 6.1.0`, `sp1-prover 6.1.0`, `sp1-verifier 6.1.0` on top of `sp1-hypercube 6.3.1`,
`sp1-recursion-machine 6.3.1`, `sp1-recursion-executor 6.3.1`, `slop-basefold 6.3.1`,
`slop-jagged 6.3.1`, `slop-multilinear 6.3.1`, `slop-sumcheck 6.3.1`. The SP1 6.1.0 facade crates sit
on the 6.3.1 Hypercube internals. The `p3-* 0.4.3-succinct` entries in the same lock (including
`p3-koala-bear`, `p3-fri`, `p3-uni-stark`) are Aere's own separate Plonky3 circuits' dependencies
(`zk-circuits/*`, the recursive-aggregation / zkml / attr provers), not the SP1 recursion proof.
Consequence for scope. The remaining work is not "add the SP1 AIR on top of a finished FRI skeleton."
It is "build a KoalaBear multilinear (BaseFold + Jagged + sumcheck + LogUp-GKR) machine verifier and
then add the SP1 recursion machine's chips on top." Of the six confirmed generic components, only the
SHAPES of three carry over (Poseidon2 sponge, Merkle tree, duplex challenger), and even those need the
KoalaBear field and the KoalaBear Poseidon2 constants. The two largest confirmed pieces, the FRI
low-degree test and the DEEP-ALI univariate quotient check, are not used by SP1 6.1.0 at all; they are
replaced by BaseFold and by sumcheck-zerocheck respectively. This is stated bluntly because it enlarges
the honest estimate (section 11) well beyond the companion spec's "3 to 4 person-weeks for the AIR."
Two clean options follow from this finding; both are out of scope to decide here but are named so the
founder can choose (section 13): retarget 0x0AE8 to the SP1 6.1.0 Hypercube recursion proof (the plan
below), or keep the existing BabyBear + FRI skeleton and point it at Aere's OWN Plonky3 STARKs (the
`zk-circuits/*` provers), which is a real but different capability and does not remove the SP1 BN254
Groth16 wrap.
Nothing in this document changes the fail-closed contract. On-chain ZK verification on Aere stays on the
classical BN254 Groth16 gateway (`0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`) until the whole verifier
is real, KAT-green against a real exported SP1 proof, externally audited, and founder-activated.
---
## 1. Exact pinned version
| item | pin | source |
|---|---|---|
| SP1 facade crates | `sp1-sdk`, `sp1-prover`, `sp1-verifier`, `sp1-zkvm`, `sp1-build` `= "=6.1.0"` | `rollup-evm-validity/{host,guest}/Cargo.toml`, `batch-prover-recovered/**/Cargo.toml` |
| SP1 upstream tag | `v6.1.0`, commit `d454975ac7c1126097e36eceda9bce2cb9899da4` | github.com/succinctlabs/sp1 release v6.1.0 |
| SP1 recursion machine (resolved) | `sp1-hypercube 6.3.1`, `sp1-recursion-machine 6.3.1`, `sp1-recursion-executor 6.3.1` | `rollup-evm-validity/host/Cargo.lock` |
| PCS / sumcheck stack (resolved) | `slop-basefold 6.3.1`, `slop-jagged 6.3.1`, `slop-multilinear 6.3.1`, `slop-sumcheck 6.3.1` | `rollup-evm-validity/host/Cargo.lock` |
| rsp executor | git `succinctlabs/rsp` tag `reth-1.9.3-sp1-6.1.0` | `rollup-evm-validity/host/Cargo.toml` |
| field | `SP1Field = KoalaBear` (p = 2^31 - 2^24 + 1 = 2130706433 = 0x7F000001), ext degree 4 | `crates/primitives/src/lib.rs` @ v6.1.0 |
| Poseidon2 | width 16, 30 total rounds, `KoalaBearDegree4Duplex` | `crates/primitives/src/lib.rs` @ v6.1.0 |
Version-skew note `[VERIFY]`: the SP1 facade crates carry `6.1.0` while the recursion/PCS internals
resolve to `6.3.1`. The port must be pinned to the exact `sp1-recursion-machine` / `slop-*` `6.3.1`
revisions that the Aere lock resolves, not to a generic "6.1.0", because chip layouts and PCS parameters
can move between patch releases.
Reference implementation to follow (not a bundled dependency): Succinct's standalone
`github.com/succinctlabs/hypercube-verifier` (crates `algebra, basefold, commit, jagged, merkle-tree,
multilinear, stacked, sumcheck, tensor, stark, recursion, primitives, ...` plus a
`jagged-polynomial-commitments.pdf`). It is a clean, self-contained verifier for exactly this proof
system and is the best porting reference. Caveat `[VERIFY]`: its README marks it a research prototype as
of 2025-05-20, so its constants and shapes must be reconciled against the pinned `6.3.1` crates before
they are trusted byte-exact.
---
## 2. What 0x0AE8 would actually verify (the recursion / shrink / wrap proof shape)
The SP1 pipeline at v6.1.0 is: core RISC-V proof (`RiscvAir<SP1Field>`, Jagged PCS + LogUp-GKR +
sumcheck over KoalaBear) -> compress (recursively verify + fold shard proofs) -> shrink (compress to a
single small shard) -> wrap (re-prove under a BN254-friendly config) -> BN254 Groth16/PLONK. The only
Shor-breakable link is the final Groth16 wrap. The post-quantum replacement 0x0AE8 targets the proof
BEFORE that wrap: the shrink shard proof (configId 1) and/or the wrap-input shard proof (configId 2).
That proof is a `ShardProof` of the SP1 recursion machine (`RecursionAir` / `CompressAir`), verified by
`verify_shard`. Its shape, from `crates/hypercube/src/verifier/shard.rs` @ v6.1.0:
- `main_commitment`: Merkle (TCS) root over Poseidon2-KoalaBear of the main trace.
- `opened_values` (`ShardOpenedValues`): per-chip trace column evaluations at the sampled multilinear
point (there is no univariate zeta; evaluations are at a random point in the boolean-hypercube
extension domain).
- `zerocheck_proof`: the constraint sumcheck (point + claimed sum) that replaces the DEEP-ALI quotient.
- `logup_gkr_proof`: the LogUp-GKR interaction/lookup proof (per-chip evaluations).
- `evaluation_proof`: the Jagged / BaseFold PCS evaluation proof binding the opened values to the
commitment.
- `public_values`: the shard public values (for the recursion machine, `RecursionPublicValues`).
`verify_shard` steps (source order): observe public values and commitments into the Poseidon2-KoalaBear
challenger; validate proof shape against the machine config in the vk (chip counts, widths, heights);
verify the LogUp-GKR proof; verify the zerocheck proof (sample constraint-merging challenge `alpha`,
check merged constraint evaluations against the sumcheck claim, cross-check against the GKR evaluations);
verify the PCS opening via `jagged_pcs_verifier.verify_trusted_evaluations`; cross-check row counts and
the global cumulative sum. Source: https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/hypercube/src/verifier/shard.rs
The precompile wire format in `pqc-fork/pq-stark/export-inner-stark-vector.md` (magic `AS1\0`, version,
configId, vkeyDigest(32), publicValues, friProof) is broadly reusable, but two of its assumptions are
wrong for 6.1.0 and MUST be corrected: the body is a Hypercube `ShardProof` (bincode of the 6.3.1 type),
not a `ShardProof<BabyBearPoseidon2>`; and the vkey digest is `hash_koalabear()` packed (KoalaBear), not
`hash_babybear()`. See section 8.
---
## 3. Component map: what is done vs what this piece needs
| layer | confirmed generic skeleton (companion spec) | SP1 6.1.0 target (this spec) | reusable? |
|---|---|---|---|
| base field | BabyBear (0x78000001), F_{p^4}, W=11 | KoalaBear (0x7F000001), F_{p^4} | structure yes, constants NO |
| hash | Poseidon2-BabyBear, 8+13 rounds, 141 consts | Poseidon2-KoalaBear, 30 rounds | structure yes, constants NO |
| transcript | duplex challenger over BabyBear Poseidon2 | duplex over KoalaBear Poseidon2 | structure yes, re-field |
| vector commit | FieldMerkleTreeMmcs (Poseidon2) | Merkle TCS (Poseidon2-KoalaBear) | mostly yes |
| low-degree test | FRI fold + query (CONFIRMED) | BaseFold multilinear (sumcheck fold) | NO, different scheme |
| PCS wrapper | (FRI is the PCS) | Stacked + Jagged over BaseFold | NO, new |
| constraint check | DEEP-ALI univariate quotient (CONFIRMED) | zerocheck (sumcheck over merged constraints) | NO, different mechanism |
| interaction arg | none (out of generic scope) | LogUp-GKR + septic degree-7 global digest | NO, new, biggest specific add |
| machine AIR | example AIRs (Fibonacci, mul) | RecursionAir chip set (section 4) | NO, program-specific |
| vk binding | not on real path | KoalaBear Poseidon2 vk digest (section 8) | NO, new |
| public values | not on real path | RecursionPublicValues (section 9) | NO, new |
Reads as: the three "shape" rows (field, hash, transcript, merkle) port with a field swap plus new
constants; every other row is either a different scheme (BaseFold vs FRI, zerocheck vs DEEP-ALI) or an
entirely new component (Jagged, LogUp-GKR, the recursion chips, vk, public values).
---
## 4. The SP1 recursion machine AIR (RecursionAir): the chip set
`RecursionAir<F: PrimeField32 + BinomiallyExtendable<D>, const DEGREE, const VAR_EVENTS_PER_ROW>`, an
AIR machine derived with `sp1_derive::MachineAir`. Source:
https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/recursion/machine/src/machine.rs
Chip files: https://github.com/succinctlabs/sp1/tree/v6.1.0/crates/recursion/machine/src/chips
Full chip set (`machine_wide_with_all_chips`): `MemoryConst`, `MemoryVar`, `BaseAlu`, `ExtAlu`,
`Poseidon2Wide`, `Poseidon2LinearLayer`, `Poseidon2SBox`, `ExtFeltConvert`, `PrefixSumChecks`,
`Select`, `PublicValues`.
The two configs 0x0AE8 would verify use a subset:
- `compress_machine`: `MemoryConst`, `MemoryVar`, `BaseAlu`, `ExtAlu`, `Poseidon2Wide`,
`PrefixSumChecks`, `Select`, `PublicValues` (8 chips).
- `wrap_machine`: `MemoryConst`, `MemoryVar`, `BaseAlu`, `ExtAlu`, `Poseidon2LinearLayer`,
`Poseidon2SBox`, `ExtFeltConvert`, `Select`, `PublicValues` (9 chips; the wide Poseidon2 is split into
linear-layer + s-box chips for the BN254-friendly wrap).
Note the departure from SP1 Turbo: there is no `FriFold`, no `ExpReverseBitsLen`, and no `BatchFRI` chip
(those existed only because Turbo's recursion verified a FRI proof in-circuit). Their absence is itself
confirmation that the recursion layer no longer runs FRI. `PrefixSumChecks` and `ExtFeltConvert` are the
new arithmetic that the BaseFold/Jagged/sumcheck verification compiles down to.
Per-chip porting notes. For each chip the port must reproduce, in KoalaBear, its column layout and its
`eval()` constraint emission order exactly (a single mis-ordered or mis-filtered constraint makes the
folded constraint silently wrong):
- `MemoryConst`, `MemoryVar` (`chips/mem/`): the recursion VM's read/write memory tables; correctness of
addresses/timestamps is enforced through the interaction bus (section 5), so most of their weight is in
the LogUp argument, not local constraints. Constraint count per row: small (a handful) `[VERIFY]`.
- `BaseAlu` (`chips/alu_base.rs`), `ExtAlu` (`chips/alu_ext.rs`): field and extension-field add/sub/mul
(and the ext ALU carries F_{p^4} arithmetic). A few constraints each `[VERIFY]`.
- `Poseidon2Wide` / `Poseidon2LinearLayer` / `Poseidon2SBox` (`chips/poseidon2_wide/`,
`poseidon2_helper/`): the Poseidon2 permutation used inside the recursion VM (this is the in-circuit
hash the recursion program itself computes). Highest per-row constraint count of the set; the wide
variant is one row per permutation, the split variants spread it across rows for the wrap. Constraint
count: large (the dominant chip) `[VERIFY]`.
- `ExtFeltConvert` (`chips/`): base/extension felt conversion glue for the wrap.
- `Select` (`chips/select.rs`): conditional selection (multiplexer) constraints.
- `PrefixSumChecks` (`chips/prefix_sum_checks.rs`): running/prefix-sum constraints used by the
sumcheck/BaseFold verification the recursion program runs; medium constraint count `[VERIFY]`.
- `PublicValues` (`chips/public_values.rs`): binds the `RecursionPublicValues` (section 9) into the trace.
The overall `DEGREE` and `VAR_EVENTS_PER_ROW` const parameters and each chip's exact constraint count
were not read line-by-line in this pass and are `[VERIFY]` / `[MEASURE]`; the ground-truth method
(section 10) emits them directly from the compiled machine, which is the only trustworthy way to count
them (do not hand-estimate).
---
## 5. The interaction argument (LogUp-GKR + septic digest): the biggest specific addition
The chips do not talk to each other through shared columns; they talk through a multiset (lookup) bus
proved with LogUp-GKR, and this is the single largest component the generic quotient check does NOT
cover. Sources: `crates/hypercube/src/logup_gkr/` and `crates/hypercube/src/lookup/`,
`crates/hypercube/src/septic_{curve,digest,extension}.rs` @ v6.1.0.
Mechanism, as read from source:
- Every send/receive interaction a chip emits is reduced to a field element via a random linear
combination of the (kind, values) tuple with verifier challenges, then batched with the RLC challenge.
- The per-shard consistency of all sends and receives is proved with LogUp-GKR (a GKR circuit over the
rational LogUp identity), whose proof is `logup_gkr_proof` in the shard proof and is verified before
the zerocheck.
- The GLOBAL bus (cross-shard, e.g. memory that persists across the shard boundary) is accumulated into
a `SepticDigest`: a point on an elliptic curve defined over the degree-7 "septic" extension of
KoalaBear (`SepticExtension`, `[u32; 7]` coordinates). Each chip contributes a curve point; the digest
is a special offset-corrected cumulative sum
`start + sum_i (digest_i - offset) + offset - start` (the offset avoids Weierstrass addition
exceptions), with start/offset points derived from sqrt(2) and sqrt(3) constants
(`CURVE_CUMULATIVE_SUM_START_*`, `DIGEST_SUM_START_*`). The verifier checks the machine-level global
cumulative sum equals the expected value (for a complete proof, the buses net to the identity).
Source: https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/hypercube/src/septic_digest.rs
Why this is the hard part. Porting it faithfully requires: the exact interaction-kind enumeration and
field encoding per chip; the exact challenge-derivation order for the RLC and batching (a transcript
contract shared with the challenger); the full LogUp-GKR verifier (layer-by-layer sumcheck over the GKR
circuit); and the septic curve arithmetic (degree-7 extension mul, curve addition, the offset-sum
convention, the two magic start points). None of it exists in the confirmed skeleton, and any off-by-one
in the interaction encoding or the GKR layer order presents as a global "everything rejects" with no
localized error. The septic curve constants and the exact interaction encoding are `[VERIFY]` /
`[MEASURE]` (emit from the compiled machine).
---
## 6. The low-degree test: BaseFold (replaces FRI), plus Stacked + Jagged
FRI is not used. The PCS is a multilinear BaseFold committed over a Merkle TCS, wrapped by a Stacked PCS
(for batching columns of different heights) and a Jagged PCS (the "pay for what you use" evaluation
argument that reduces jagged, variable-length columns to a single multilinear evaluation via sumcheck).
Sources: `crates/prover/src/recursion.rs` (`RecursiveBasefoldVerifier`, `RecursiveMerkleTreeTcs`,
`RecursiveStackedPcsVerifier`, `RecursiveJaggedPcsVerifier`); reference crates
`hypercube-verifier/crates/{basefold, stacked, jagged, tensor, multilinear, sumcheck, merkle-tree}`.
What the port needs:
- BaseFold verifier: the multilinear analogue of FRI. Commit-phase folding driven by sumcheck rounds
(not the FRI fold relation), query openings against the Merkle TCS at each folded layer, final
evaluation check. The confirmed FRI `verify_query` does not apply; this is a new verifier.
- Stacked PCS: batches the per-chip column groups (different heights) into one commitment; the verifier
reconciles the per-group claims.
- Jagged PCS: `verify_trusted_evaluations` runs the jagged sumcheck that binds the opened multilinear
evaluations (`opened_values`) to the committed data at the sampled point. This is where the bulk of
the PCS soundness lives.
- Parameters (`[MEASURE]`): the BaseFold log_blowup / folding-arity / query count / proof-of-work bits
for the compress, shrink, and wrap configs, and the resulting soundness (score with the Hypercube
soundness analysis, not ethSTARK soundcalc, since this is not FRI). Whether the wrap config
(`CircuitWrapConfig`) uses different PCS parameters or a different field embedding than compress/shrink
is `[VERIFY]`.
---
## 7. The constraint check: zerocheck sumcheck (replaces DEEP-ALI quotient)
The confirmed generic quotient-consistency identity (`folded_constraints * inv_zeroifier == quotient`
from `p3-uni-stark`) is a UNIVARIATE-STARK mechanism and is not what SP1 6.1.0 checks. Instead the merged
AIR constraint is verified by a sumcheck (zerocheck): the verifier samples the constraint-merging
challenge `alpha`, the prover claims the merged constraint polynomial sums to zero over the boolean
hypercube, and the sumcheck reduces that claim to a single evaluation at a random point, which is then
opened through the PCS (section 6) and cross-checked against the LogUp-GKR evaluations. Source:
`crates/hypercube/src/verifier/shard.rs` verify step 4, and `hypercube-verifier/crates/stark/folder.rs`
+ `sumcheck`.
The port needs: the sumcheck verifier (round polynomials, challenge derivation, final-evaluation
reduction); the constraint folder that reproduces each chip's `eval()` emission order and its
first-row/last-row/transition filters in the multilinear setting; and the binding of the sumcheck's
final point to the PCS opening. The generic DEEP-ALI code is not reusable here beyond the high-level idea
of merging constraints with a random `alpha`.
---
## 8. The verifying key (vk) and its digest binding
`MachineVerifyingKey` fields (from `crates/hypercube/src/verifier/hashable_key.rs` @ v6.1.0):
`preprocessed_commit` (Merkle root of the preprocessed/selector trace), `pc_start`,
`initial_global_cumulative_sum` (a septic curve point, x and y), `enable_untrusted_programs`, plus the
machine shape (chip ordering, widths, heights) carried by the associated `Machine`. Source:
https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/hypercube/src/verifier/hashable_key.rs
Digest binding. `HashableKey` provides `hash_koalabear()` (Poseidon2 over KoalaBear of
`[preprocessed_commit, pc_start, initial_global_cumulative_sum.x, initial_global_cumulative_sum.y,
enable_untrusted_programs]`, producing an 8-element SP1Field digest), `hash_u32()`, `hash_bytes()`, and
`hash_bn254()` (via `koalabears_to_bn254()`, the digest the on-chain Groth16 path currently binds).
For 0x0AE8 the verifier must (a) recompute `hash_koalabear()` over the supplied vk fields, (b) check it
equals the 32-byte `vkeyDigest` in the AS1 wire envelope, and (c) use the vk's machine shape to drive the
shape checks in `verify_shard`. The export doc's `vk.hash_babybear()` is WRONG for 6.1.0 and must be
`hash_koalabear()`; the packing is 8 KoalaBear limbs, big-endian per limb. `[VERIFY]` the exact limb
order and the exact field list that feeds `hash_koalabear` at the pinned 6.3.1 revision.
---
## 9. The public-values layout
`RecursionPublicValues<T>` (from `crates/recursion/executor/src/public_values.rs` @ v6.1.0), field type
KoalaBear (`T = SP1Field`) on the real path. Fields (source order, as read):
`prev_committed_value_digest`, `committed_value_digest`, `prev_deferred_proofs_digest`,
`deferred_proofs_digest`, `pc_start`, `next_pc`, `initial_timestamp`, `last_timestamp`, memory/page
indices, `sp1_vk_digest`, `vk_root`, `global_cumulative_sum` (septic point), `digest`, `proof_nonce`,
`contains_first_shard`, `num_included_shard`, `is_complete`, `exit_code`, `commit_syscall`,
`commit_deferred_syscall`. Total element count is `RECURSIVE_PROOF_NUM_PV_ELTS =
size_of::<RecursionPublicValues<u8>>()`; the exact integer is `[VERIFY]` (emit from the compiled crate).
Source: https://github.com/succinctlabs/sp1/blob/v6.1.0/crates/recursion/executor/src/public_values.rs
The verifier must: parse these from the wire `publicValues`; observe them into the challenger in the
exact source order (they bind the transcript, so a wrong order diverges every challenge); check the
internal `digest` consistency; and expose `committed_value_digest` / `sp1_vk_digest` / `vk_root` /
`is_complete` / `exit_code` as the verified outputs the AerePQStarkVerifier adapter returns. The
committed-value digest and `sp1_vk_digest` are what a caller uses to bind a specific guest program and
its output, exactly as the BN254 path does today.
---
## 10. Ground-truth method (the only trustworthy validation)
Every constraint count, parameter, and layout marked `[VERIFY]` / `[MEASURE]` above must be produced from
the compiled pinned toolchain, never hand-derived. The method, on a throwaway non-infra prover box (never
the live infra host, never a validator; see the no-heavy-compute-on-infra rule):
1. Pin a Rust workspace to the exact resolved revisions in `rollup-evm-validity/host/Cargo.lock`:
`sp1-sdk = "=6.1.0"`, and let it resolve `sp1-hypercube`, `sp1-recursion-machine`,
`sp1-recursion-executor`, `slop-basefold`, `slop-jagged`, `slop-multilinear`, `slop-sumcheck` at
`6.3.1`. Vendor them for reproducibility.
2. Emit machine metadata directly: for `compress_machine()` and `wrap_machine()`, print each chip's name,
width, and its symbolic constraint count and max degree; print `RECURSIVE_PROOF_NUM_PV_ELTS`; print the
BaseFold/Jagged config parameters; print the septic curve constants and the interaction-kind encoding.
This replaces every `[VERIFY]` count with a measured one.
3. Produce a real proof: run a tiny guest (fibonacci or `aere-client`) through
`prove(...).compressed()`, stopping before the Groth16 wrap; serialize the shrink `ShardProof`
(bincode), the vk, and its `hash_koalabear()` digest and `public_values`. This is the
`sp1_shrink_valid_01.bin` corpus vector (correcting the export doc to KoalaBear + Hypercube).
4. Instrument `verify_shard` to dump the exact challenger transcript (observe order, sampled `alpha`, the
sumcheck points, the query indices) so each ported sub-verifier can be diffed byte-for-byte against
the reference, the same KAT discipline the generic components used.
Follow the `hypercube-verifier` reference crate structure (section 1) so the port is a
component-by-component mirror of a maintained verifier, not a from-scratch reading of the prover.
---
## 11. Effort, hardest parts, honest estimates
Realistic single-specialist estimates (STARK/sumcheck specialist with the SP1 toolchain). These are
deliberately not optimistic; the two generic pieces the companion spec counted as done (FRI, DEEP-ALI)
do not carry over, so the remaining surface is larger than that spec's "3 to 4 weeks for the AIR."
| piece | effort (person-weeks) | risk |
|---|---|---|
| KoalaBear field + F_{p^4} (re-field the confirmed BabyBear code, new prime/constants) | ~0.5 to 1 | low |
| Poseidon2-KoalaBear (width 16, 30 rounds, new constants) + duplex challenger re-field | ~1 to 1.5 | medium (constants) |
| Merkle TCS over KoalaBear (re-field the confirmed MMCS) | ~0.5 to 1 | low |
| BaseFold multilinear verifier (new, replaces FRI) | ~3 to 4 | high |
| Stacked + Jagged PCS verifier (new, the jagged sumcheck) | ~3 to 4 | high |
| sumcheck + zerocheck constraint check (new, replaces DEEP-ALI) | ~2 to 3 | high |
| LogUp-GKR + septic degree-7 global digest (new, biggest specific add) | ~3 to 4 | HARDEST |
| RecursionAir chip constraints (8 to 11 chips, KoalaBear, exact eval order) | ~3 to 4 | high |
| vk binding (`hash_koalabear`) + RecursionPublicValues + shape checks | ~1 to 2 | medium |
| wire parser (`WireReader` for the Hypercube ShardProof) + adapter wiring | ~1 to 2 | medium |
| integration, gas benchmark under EIP-7825, KAT corpus, soundness scoring, audit prep | ~4 to 6 | high |
| total | ~22 to 32 person-weeks | multi-month |
That is roughly 5 to 8 months of focused single-specialist work plus an external audit, materially more
than the companion spec's figure because the target is Hypercube, not Turbo. Hardest parts, ranked:
(1) LogUp-GKR + septic digest interaction argument (section 5); (2) BaseFold + Jagged PCS (section 6);
(3) the sumcheck zerocheck constraint mechanism (section 7); (4) the RecursionAir chips at exact eval
order (section 4). Each is multi-week on its own.
Given this, the design doc's recommendation (companion spec section 5.1) is even more compelling: wrap a
pinned, reproducible NATIVE build of the maintained Rust verifier (the `hypercube-verifier` crates or the
`sp1-verifier 6.1.0` inner path) as the precompile's crypto core, rather than hand-port a KoalaBear
BaseFold + Jagged + sumcheck + LogUp-GKR verifier into Java. A hand port multiplies the audit surface and
the "two wrong copies agree" risk across every one of the pieces above.
---
## 12. End-to-end conformance KAT (the final gate)
The single test that proves the whole verifier, once the pieces above exist:
1. Export a real SP1 6.1.0 shrink (configId 1) and wrap-input (configId 2) proof + vk + public values by
the section-10 method; wrap each into the AS1 envelope (KoalaBear `hash_koalabear` vkey digest).
2. `sp1_shrink_valid_01.bin`, `sp1_wrap_valid_01.bin`: 0x0AE8 must ACCEPT (return the verified
committed-value digest / vk digest), and the SAME public values verified through the live BN254
gateway (`0x9ca479...`) must agree on accept.
3. Negatives, each must REJECT (return EMPTY): flip one byte inside a query opening
(`*_tampered_*`); mutate one byte of `publicValues` (`*_wrongpub_*`, the transcript binds them);
swap in a vk with a different `hash_koalabear` digest (`*_wrongvk_*`); tamper one sumcheck round
polynomial and one LogUp-GKR layer (`*_badsumcheck_*`, `*_badgkr_*`).
4. Cross-field regression: the confirmed BabyBear + FRI skeleton must continue to reject these KoalaBear
Hypercube inputs (it is a different verifier), confirming the two are not conflated.
Until a real exported SP1 6.1.0 proof exists and passes this, the precompile verifies nothing and stays
fail-closed. There is no offline substitute for this KAT.
---
## 13. Bottom line for the whole #2 effort
- What is real today: a BabyBear + FRI + univariate-STARK verifier SKELETON with six conformance-confirmed
generic components (field, Poseidon2, MMCS, FRI, transcript, DEEP-ALI quotient) against Plonky3
`0.4.3-succinct`. That is genuine, tested work, and it is the correct verifier for Aere's OWN Plonky3
STARKs (`zk-circuits/*`).
- What is NOT true: that this skeleton is most of an SP1 6.1.0 verifier. SP1 6.1.0 is Hypercube. Its
inner recursion proof is a KoalaBear multilinear proof (BaseFold + Stacked + Jagged PCS, sumcheck
zerocheck, LogUp-GKR with a degree-7 septic global digest) over the `RecursionAir` chip set. The FRI
and DEEP-ALI pieces do not apply to it; only the Poseidon2/Merkle/challenger SHAPES carry over, and
those need the KoalaBear field and constants.
- What stands between here and a live native post-quantum STARK verifier that replaces the classical
BN254 Groth16 gateway: (1) a KoalaBear re-field of the shape components; (2) the four new components
(BaseFold, Jagged/Stacked, sumcheck-zerocheck, LogUp-GKR + septic digest); (3) the RecursionAir chips;
(4) the vk binding and RecursionPublicValues; (5) a real exported SP1 6.1.0 end-to-end proof KAT; (6)
soundness scoring and an EIP-7825 gas benchmark; (7) an external audit; (8) founder activation.
- Realistic total remaining: on the order of 22 to 32 person-weeks (roughly 5 to 8 months) of a
STARK/sumcheck specialist plus an external audit, whether hand-ported or (recommended) wrapped as a
pinned native build of the maintained Rust verifier. The top-level 0x0AE8 stays fail-closed the entire
time; on-chain ZK verification remains classical BN254 Groth16 until all eight gates are met and the
founder activates.
---
## 14. Open `[VERIFY]` / `[MEASURE]` flags
1. `[VERIFY]` Version skew: pin the exact `sp1-recursion-machine` / `slop-* 6.3.1` revisions the Aere
lock resolves; the `6.1.0` facade sits on `6.3.1` internals.
2. `[VERIFY]/[MEASURE]` Per-chip constraint counts, `DEGREE`, `VAR_EVENTS_PER_ROW` (emit from the
compiled machine, section 10).
3. `[VERIFY]/[MEASURE]` `RECURSIVE_PROOF_NUM_PV_ELTS` exact integer and the exact PV field ordering.
4. `[VERIFY]/[MEASURE]` BaseFold / Jagged parameters (blowup, folding arity, query count, pow bits) for
compress, shrink, and wrap; whether `CircuitWrapConfig` differs.
5. `[VERIFY]/[MEASURE]` Septic curve constants (a, b, sqrt(2)/sqrt(3) start points) and the exact
per-chip interaction-kind encoding + challenge order.
6. `[VERIFY]` Exact `hash_koalabear()` input field list and limb packing at 6.3.1.
7. `[VERIFY]` KoalaBear Poseidon2 external/internal round split within the 30 total rounds, and its
constants.
8. `[VERIFY]` Exact-constant conformance of the `hypercube-verifier` reference (2025-05-20 prototype)
against the pinned 6.3.1 crates.
9. `[MEASURE]` The end-to-end KAT (section 12): needs a real exported SP1 6.1.0 proof; nothing offline
substitutes for it.
## 15. Files
- `docs/AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md` (this document).
- `docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (the generic skeleton + its confirmed status; note its
BabyBear/FRI premise for SP1 6.1.0 is corrected here).
- `pqc-fork/pq-stark/export-inner-stark-vector.md` (real-vector export; correct `hash_babybear` ->
`hash_koalabear` and the ShardProof type to the Hypercube 6.3.1 type per section 8).
- `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java` (fail-closed skeleton; unchanged).
- `contracts/contracts/zkverify/AerePQStarkVerifier.sol` (fail-closed adapter; unchanged).

View File

@ -0,0 +1,96 @@
# Component (f): Fiat-Shamir transcript / Poseidon2 duplex challenger (0x0AE8) - CONFORMANCE-CONFIRMED
> **Scope caveat (2026-07-19 finding).** This transcript is conformance-confirmed against Plonky3
> `p3-challenger` / `p3-fri` `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1
> 6.1.0. SP1 6.1.0 is a **Hypercube** release (KoalaBear multilinear: BaseFold + Jagged +
> sumcheck-zerocheck + LogUp-GKR), so FRI and DEEP-ALI do not apply to it and this skeleton does not
> verify SP1 6.1.0 proofs. (The duplex-challenger SHAPE carries over to SP1's Hypercube stack, but
> over the KoalaBear field with KoalaBear constants and a different observe/sample order.) Replacing
> the SP1 BN254 Groth16 wrap is a separate ~22 to 32 person-week retarget. See
> `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
Date: 2026-07-19. Precompile: Aere PQ STARK verifier at 0x0AE8. Scope: port-spec component (f).
## Result headline
- Transcript conformance KAT: **PASSED** (PASS=44 FAIL=0 across Python + Node + standalone Java;
standalone `ChallengerSelfTest` PASS=31 including a driver fail-closed gate model).
- It **closed the FRI betas/indices loop** that component (d) left open: transcript -> derived
betas/indices -> confirmed `Fri.verifyQuery` ACCEPTs the real FRI ground-truth proof and REJECTs a
tampered beta.
- `Challenger.spongePorted` flipped **true**. Top level stays **FAIL-CLOSED**: `StarkConstraints.
evaluateAtZeta` still returns UNAVAILABLE (component (e), the AIR, un-ported), gating ACCEPT before
the query loop and before the single `return ACCEPT`. `computePrecompile` returns EMPTY for every
input; ACCEPT is unreachable by construction. DO NOT ACTIVATE.
## What was confirmed from pinned source (p3-challenger / p3-fri 0.4.3-succinct)
`DuplexChallenger<BabyBear, Perm, WIDTH=16, RATE=8>` (duplex_challenger.rs) + `GrindingChallenger`
(grinding_challenger.rs), transcript order from p3-fri verifier.rs `verify_shape_and_sample_challenges`:
- `observe(felt)`: clear output buffer, push to input buffer, duplex when input buffer reaches RATE=8.
- `duplexing`: overwrite the first `input_buffer.len()` (<=RATE) of 16 lanes, permute (confirmed
Poseidon2), set output buffer = lanes [0..8).
- `sample`: duplex if inputs buffered OR outputs empty, then `Vec::pop` (LIFO, from the END). An
F_{p^4} sample = 4 sequential base pops [c0,c1,c2,c3] (`sample_ext_element` = `sample_vec(4)` then
`from_base_slice`).
- `sample_bits(bits)`: LOW `bits` of `as_canonical_u64` (confirmed LSB).
- `check_witness(pow_bits, witness)`: `observe(witness)` then `sample_bits(pow_bits) == 0`.
- FRI transcript order: per commit -> observe 8-elem digest, sample beta (F_{p^4}); then observe
final_poly (4 coords); then check_witness(pow_bits, pow_witness); then num_queries x
sample_bits(log_max_height). log_max_height = commits.len() + log_blowup.
Montgomery/serialization: all arithmetic canonical (the confirmed Poseidon2 reference already carries
the internal-layer R^-1 factor); the extractor emits `as_canonical_u32` and `as_base_slice` coords, and
the references reproduce them exactly.
## Ground truth (executed the pinned crates)
`pqc-fork/pq-stark/challenger-extractor/` (Cargo.toml + copied fri-extractor Cargo.lock, byte-identical
pins) compiled and ran (target dir in scratch, no repo pollution, live infra untouched). It emits
`challenger_ground_truth.json` with two blocks:
1. `duplex_kat`: a fully-scripted observe/sample transcript -> base samples, F_{p^4} ext samples,
sample_bits, the full 16-lane sponge state at a checkpoint, and a check_witness accept/reject table
(real grinding witness accepts, fixed non-witnesses reject).
2. `fri_transcript`: the SAME three FRI cases as `fri_ground_truth.json`, rebuilt (same seeds) so the
commit_phase_commits / final_poly / pow_witness / betas / query_indices match; the references derive
betas+indices+PoW-accept from (commits, final_poly, pow_witness) and match BOTH the extractor AND
fri_ground_truth.json.
The references reproduce every value byte-for-byte (all 3 languages identical), and the derived
betas/indices, fed to the confirmed `Fri.verifyQuery`, accept the real proof and reject a tampered beta.
## Files added/changed
Added: `challenger_reference.py`, `challenger_reference.mjs`, `ChallengerSelfTest.java`,
`test_challenger.py`, `challenger-extractor/{Cargo.toml,Cargo.lock,src/main.rs}`,
`challenger_ground_truth.json`, `../results/kat-results-challenger.json`.
Changed:
- `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java`: `Challenger` class rewritten as the
confirmed duplex sponge over `Poseidon2Bb.permute` (observeField/sampleBaseField/sampleExtField/
sampleBits/checkWitnessField + byte-facing driver adapters); `spongePorted = true`; driver stage-4
comment clarifies the AIR gate; stale "spongePorted=false" docs updated. `StarkConstraints.
evaluateAtZeta` UNCHANGED (still returns UNAVAILABLE at line ~1077).
- `docs/AERE-STARK-VERIFIER-PORT-SPEC.md`: status header, section 5 note, section 7 rewritten
(IMPLEMENTED + CONFORMANCE-CONFIRMED), section 9 table row (f), section 10 files.
- `pqc-fork/pq-stark/README.md`: HONEST STATUS, sections (b)/(c)/(d) closings, new section (f), layout.
- `pqc-fork/pq-stark/test_fri_verify.py`: descriptive notes updated (f now confirmed; loop closed).
## Fail-closed proof
verify() control flow: ACCEPT requires stage 3 (PoW) AND stage 4 (AIR) AND stage 5 (queries) all pass.
Stage 4 = `StarkConstraints.evaluateAtZeta` ALWAYS returns UNAVAILABLE (component (e) un-ported) and is
checked (line 196) BEFORE the query loop and BEFORE the single `return ACCEPT` (line 228). So verify()
returns UNAVAILABLE for every input reaching stage 4; computePrecompile returns EMPTY. Flipping
spongePorted cannot create an ACCEPT path. Corroborated at runtime by `ChallengerSelfTest.gateModel`
(driver.gate.ACCEPT_unreachable_for_every_input PASS). The precompile is not compiled here (it is
applied via patch to a Besu build, no local Besu jar); the challenger algorithm is verified by the
byte-identical standalone `ChallengerSelfTest`.
## Remaining
Component (e), the SP1 recursion AIR (StarkConstraints, ~3-4 person-weeks, HARDEST), is the one
remaining un-ported crypto-core component. Real exported SP1 v6.1.0 transcript order is [MEASURE].
On-chain ZK verifiers remain classical BN254 until all 6 components + integration + a real SP1-proof
end-to-end KAT + audit + founder activation.

View File

@ -0,0 +1,751 @@
# AERE STARK verifier port specification (0x0AE8): component order, interfaces, vectors, effort
> **SCOPE CAVEAT (research finding, 2026-07-19; read before anything else).** The six generic
> components below are real and conformance-confirmed, but they were confirmed against Plonky3
> `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack, the `zk-circuits/*` provers), NOT against
> SP1 6.1.0. The pinned SP1 (facade `= "=6.1.0"`, resolved internals `sp1-hypercube 6.3.1`) is a
> **Hypercube** release, not Turbo: its inner recursion proof is a **KoalaBear multilinear** proof
> (BaseFold + Jagged/Stacked PCS + sumcheck-zerocheck + LogUp-GKR with a septic-curve global digest),
> NOT a BabyBear FRI `ShardProof`. FRI and the DEEP-ALI univariate quotient check, the two largest
> confirmed pieces here, do NOT apply to SP1 6.1.0 at all (they are replaced by BaseFold and by
> sumcheck-zerocheck). So this document specifies a real BabyBear+FRI verifier skeleton that verifies
> Aere's own Plonky3 STARKs; it does NOT, and completing the "SP1 recursion AIR" line below would NOT,
> verify an SP1 6.1.0 proof. Replacing the BN254 Groth16 wrap on real SP1 proofs is a SEPARATE
> ~22 to 32 person-week effort and a founder retarget decision, specified in
> `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md` and `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md`.
> Read the whole of this document with "SP1 6.1.0 inner STARK" mentally replaced by "a BabyBear + FRI
> STARK (Aere's own Plonky3 circuits)", except where the retarget summary is cited.
Spec date: 2026-07-19
Status: PORT PLAN + sub-components implemented and tested; the Poseidon2 permutation (component (b)),
the MMCS vector commitment (component (c)), the FRI FOLD + OPENING relation / low-degree test
(component (d), verify_query), the Fiat-Shamir duplex-sponge transcript / challenger (component (f)),
AND now the GENERIC AIR constraint / quotient-consistency MECHANISM (component (e), the DEEP-ALI
identity) are all CONFORMANCE-CONFIRMED against the pinned Plonky3 (a real known-answer test PASSES for
each). With component (e)'s generic mechanism confirmed, all SIX generic components of a BabyBear STARK
verifier skeleton are now conformance-confirmed. The precompile 0x0AE8 is STILL a REFERENCE SKELETON
that verifies NOTHING for a real SP1 proof and fail-closes (returns EMPTY for every input), because the
SP1-recursion-SPECIFIC AIR (its exact constraint set + interactions + public-value layout) and the vkey
digest that binds it are NOT ported. This document does not change the fail-closed contract. It
specifies, component by component, the multi-week cryptography port that would make 0x0AE8 a real
post-quantum STARK verifier, and it records the self-contained sub-components implemented and tested so
far: the BabyBear field F_p and its degree-4 extension F_{p^4} (section 2, component (a), the
FOUNDATION), the Poseidon2 permutation over BabyBear (section 3, component (b), the hash, now
conformance-confirmed), the MMCS / FieldMerkleTreeMmcs vector commitment (section 4, component (c), now
conformance-confirmed), the FRI query-index derivation (section 8, one slice of component (d)), the
full FRI fold + opening verify_query (section 5, component (d), now conformance-confirmed against the
pinned p3-fri), and the GENERIC AIR quotient-consistency check (section 6, component (e)'s generic
mechanism, now conformance-confirmed against the pinned p3-uni-stark). These are building blocks, NOT a
working verifier: (b), (c), (d)'s fold+opening, (f) the duplex-sponge Fiat-Shamir transcript, and (e)'s
generic quotient identity now match Plonky3 (real KATs passed). Component (f) DERIVES the FRI betas /
query indices / grinding IN-CIRCUIT from the transcript, and that closes the loop the FRI KAT left open:
the derived betas/indices, fed into the confirmed (d) verify_query, accept the real FRI ground-truth
proof and reject a tampered beta (pq-stark/test_challenger.py, PASS=44). Component (e)'s generic quotient
check reproduces the pinned p3-uni-stark verifier's ACCEPT on two known example AIRs (Fibonacci +
degree-3 mul) and rejects tampered openings/quotient/alpha (pq-stark/test_air_quotient.py, PASS=18). The
remaining un-ported crypto-core piece is the SP1-recursion-SPECIFIC AIR + vkey binding (part of component
(e), section 6.2), so the top level STAYS FAIL-CLOSED for real SP1 proofs: with
StarkConstraints.SP1_RECURSION_AIR_PORTED = false, StarkConstraints.evaluateAtZeta returns UNAVAILABLE at
the constraint stage, before the query loop and before the single return ACCEPT, so verify() returns
EMPTY for every real input.
Companion documents (read together):
- `docs/PQ-STARK-VERIFIER-PRECOMPILE-2026-07-18.md` (design, encoding, soundness, gas).
- `docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md` (gated activation plan).
- `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java` (the fail-closed skeleton).
- `contracts/contracts/zkverify/AerePQStarkVerifier.sol` (the fail-closed ISP1Verifier adapter).
## 0. Honest scope and non-claims (read first)
- The top-level verifier does NOT work. It has never verified a real STARK proof and it cannot in
this build. `Sp1StarkVerifierPrecompiledContract.verify(...)` reaches the delegated crypto core,
which returns UNAVAILABLE, so the driver returns EMPTY. The single `return ACCEPT` line is
unreachable. Nothing in this document makes it reachable.
- This is a multi-week port, stated plainly. The realistic effort for one STARK/FRI specialist is on
the order of 3 to 4 months (roughly 11 to 16 person-weeks of focused work plus audit), and the two
hardest components (the SP1 recursion AIR constraint evaluation and the FRI low-degree test) are
each multi-week on their own. See section 9.
- Until this port is complete, externally audited, and founder-activated, the on-chain ZK verifiers
on Aere remain the CLASSICAL BN254 Groth16 gateway (`0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`).
That path is Shor-breakable. We say this plainly. 0x0AE8 is a post-quantum verifier skeleton being
built, not a live capability, and (per the scope caveat at the top) the skeleton specified here
targets BabyBear + FRI STARKs, i.e. Aere's own Plonky3 circuits, NOT SP1 6.1.0 proofs; replacing
the SP1 BN254 Groth16 wrap is a separate founder-gated retarget to the SP1 6.1.0 Hypercube stack.
- Consensus on chain 2800 is classical secp256k1 ECDSA QBFT (N=7, f=2). This precompile is an
EVM-layer verifier and changes none of that.
- `[VERIFY]` marks a constant, round count, or convention that must be confirmed against the pinned
reference before it can be trusted (it is NOT invented here). `[MEASURE]` marks a validation that
requires running the pinned reference or the SP1 prover and cannot be produced offline in this pass.
## 1. What is being verified, and the port decomposition
The target this document was originally written against is a BabyBear + FRI STARK: a Plonky3
`ShardProof` over the `BabyBearPoseidon2` config. **Correction (see the scope caveat at the top):**
the 2026-07-19 finding established that SP1 6.1.0 does NOT produce such a proof (it is Hypercube /
KoalaBear multilinear), so this exact `ShardProof<BabyBearPoseidon2>` target is Aere's OWN Plonky3
circuits (`zk-circuits/*`), not SP1 6.1.0. Verifying such a BabyBear+FRI STARK directly on-chain is a
real capability, but it does NOT remove the BN254 Groth16 wrap from an SP1 6.1.0 proof, because SP1
6.1.0 has no BabyBear FRI `ShardProof` in its chain. Removing the SP1 Groth16 wrap is the separate
Hypercube retarget (`AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`). Read the SP1-framed
sentences below with that substitution in mind.
A faithful verifier decomposes into six components with a strict dependency order. Each higher
component consumes the ones below it, so they MUST be ported bottom-up and each validated against
its own vectors before the next is trusted.
```
(f) Fiat-Shamir transcript (Poseidon2 duplex challenger) <- pervasive, threads through d and e
(e) AIR constraint evaluation + SP1 recursion vk binding HARDEST
(d) FRI folding + query-index derivation + low-degree test HARD <- section 8 implements one slice
(c) Merkle / FRI vector-commitment scheme
(b) Poseidon2 permutation over BabyBear (the hash) error-prone (constants)
(a) BabyBear field p = 2^31 - 2^27 + 1 and its degree-4 extension FOUNDATION
```
Recommended port order: (a) then (b) then (f, the transcript, since it is just a Poseidon2 sponge)
then (c) then (d) then (e). Component (f) is listed last in the decomposition because it is
conceptually the glue, but in build order it comes right after Poseidon2 because everything above it
needs a working challenger to derive the same randomness the prover used.
The rest of this section is one subsection per component: what it does, its interface, the exact
test vectors that validate it and where they come from, an effort estimate, and the parts that are
hardest or still `[VERIFY]`.
## 2. Component (a): BabyBear field and degree-4 extension
What it does. Provides the base field F_p, p = 15 * 2^27 + 1 = 2013265921 = 0x78000001 (a 31-bit
prime with 2^27 | (p-1), giving a large smooth two-adic subgroup for evaluation domains), and the
degree-4 binomial extension F_{p^4} = F_p[x]/(x^4 - W). |F_{p^4}| is approximately 2^124.05. FRI
folding challenges, the DEEP out-of-domain point zeta, and query soundness all live in F_{p^4}.
Interface.
- `Fp`: add, sub, mul, neg, inv (Fermat a^(p-2)), pow, from/to canonical u32, two-adic generator of
the order-2^27 subgroup and its powers.
- `Fp4`: add, sub, mul (schoolbook convolution reduced by x^4 = W), inv, Frobenius, base embedding.
State in the skeleton (IMPLEMENTED + TESTED, 2026-07-19). `BabyBear` is COMPLETE (canonical,
non-Montgomery, 64-bit intermediates): add, sub, neg, mul, pow, inv (Fermat a^(p-2)), plus the
multiplicative generator (`GENERATOR = 31`) and the two-adic generator of the order-2^27 subgroup
(`twoAdicGenerator(bits)`). `BabyBearExt4` is now COMPLETE: `W` is PINNED to 11, and add, sub, neg,
mul (schoolbook convolution reduced by x^4 = W), inv (Fermat a^(p^4-2)), Frobenius (a^p), and the
base embedding are all implemented. The former `W_UNSET`/`TODO(port)` guard is removed. Wiring the
completed field into the precompile does NOT make ACCEPT reachable: the field sits behind the
still-UNAVAILABLE crypto core (Poseidon2, FRI fold arithmetic, AIR), so the top level still returns
EMPTY for every input and the "DO NOT ACTIVATE / verifies nothing" contract is unchanged.
Validated offline (`pqc-fork/pq-stark/test_babybear_field.py`, ran 2026-07-19, PASS=396242 FAIL=0,
Python + Node + Java byte-identical; standalone `BabyBearFieldSelfTest` PASS=360018 FAIL=0):
- F_p and F_{p^4} field axioms (comm/assoc/distrib/identities/inverses) over many random cases;
- Fermat inverse a^(p-2) equals an INDEPENDENT extended-Euclid bignum inverse (Python `pow(a,-1,p)`);
- `GENERATOR = 31` has multiplicative order exactly p-1 (proven via the known p-1 = 2^27 * 3 * 5);
- `twoAdicGenerator(k)` has order exactly 2^k for k = 1..27;
- `W = 11` is a quadratic non-residue and p == 1 mod 4, so x^4 - 11 is IRREDUCIBLE over F_p
(Lidl-Niederreiter binomial criterion), i.e. F_{p^4} is a genuine field; x^4 == W holds in F_{p^4};
- Frobenius^4 = identity, Frobenius fixes the base field, base embedding is a ring homomorphism.
`[VERIFY]` items (the field is real and self-consistent; these are the conformance-to-Plonky3 gaps).
- `W = 11`: PROVEN here to yield an irreducible x^4 - 11 (hence a real field), but that 11 is
Plonky3's exact `BinomialExtensionField<BabyBear, 4>` non-residue is `[VERIFY]`/`[MEASURE]` against
`p3_baby_bear` at the pinned SP1 v6.1.0 revision. A wrong-but-still-irreducible W builds a valid
field that silently DISAGREES with the prover, so irreducibility is necessary, not sufficient.
- `GENERATOR = 31`: PROVEN here to have order p-1, but that it is Plonky3's chosen multiplicative
generator (and that `twoAdicGenerator(27) = 440564289` is Plonky3's stored root of unity) is
`[VERIFY]`. The ORDER is a proven field fact; the exact VALUE is not confirmed offline.
- Whether the production port uses Montgomery form (Plonky3 does, `MontyField31`) for performance.
Canonical form is correct and simpler; Montgomery is a perf optimization to benchmark, not a
correctness requirement.
Test vectors.
- Field identities (a * inv(a) = 1, distributive/associative laws, generator order p-1, subgroup
generator has order 2^27). Self-checkable offline, no external source. IMPLEMENTED + PASSING
(`test_babybear_field.py`; `Sp1StarkVerifierKat.fieldArithmeticKats` also asserts a subset).
- Extension: (x^4 - W) irreducibility for the pinned W; Fp4 inverse round-trips; Frobenius^4 = id.
IMPLEMENTED + PASSING.
- Conformance: a handful of `p3-baby-bear` / `p3-field` unit outputs (mul, inv, extension mul) for
fixed inputs, cross-checked byte-for-byte. Source: Plonky3 `p3-baby-bear` tests. `[MEASURE]`
(needs the pinned Plonky3 source; not fetched offline in this pass, marked not faked).
Effort. About 0.5 person-week. Low risk. IMPLEMENTED + TESTED in this pass; the remaining work is the
`[VERIFY]`/`[MEASURE]` conformance of W / generator against Plonky3 and the Montgomery-vs-canonical
gas decision.
## 3. Component (b): Poseidon2 permutation over BabyBear (the hash)
What it does. The fixed-width algebraic permutation that Plonky3 uses as both the Merkle/vector
commitment compression function and the duplex-sponge Fiat-Shamir challenger. Everything hashed in
the proof (commitments, transcript) is Poseidon2 over BabyBear.
Parameters (Plonky3 `Poseidon2BabyBear`, the SP1 config).
- Width t = 16 field elements. `[VERIFY]` (SP1's BabyBear Poseidon2 permutation width; 16 is the
Plonky3 default used by the recursion config).
- S-box: x^7. Degree 7 is the smallest exponent d > 1 with gcd(d, p-1) = 1: p-1 = 2^27 * 3 * 5, so d
must avoid factors 2, 3, 5, and the smallest such d is 7. x^7 is therefore a bijection on F_p. This
is number-theoretically CONFIRMED offline (the section-3 harness asserts d=7 is the minimal coprime
degree and that the monomial inverse round-trips). This one is solid, not `[VERIFY]`.
- External (full) rounds: 8 (4 initial + 4 terminal), applying the S-box to all 16 lanes. CONFIRMED
(`poseidon2_round_numbers_128(16, 7)` returns `(8, 13)`).
- Internal (partial) rounds: 13, applying the S-box to lane 0 only. CONFIRMED
(`poseidon2_round_numbers_128(16, 7)` = `(8, 13)`).
- External linear layer: the Poseidon2 M_E matrix, built from the fixed MDS matrix
M4 = [[2,3,1,1],[1,2,3,1],[1,1,2,3],[3,1,1,2]] in the MDS-light block construction (diagonal blocks
2*M4, off-diagonal blocks M4). CONFIRMED: the full 16x16 external matrix recovered from Plonky3's
`Poseidon2ExternalMatrixGeneral` (via basis-vector probes) equals this exact block form.
- Internal linear layer: **M_I = R^{-1} * (J + diag(D))** (J = all-ones), applied as
state[i] = R_INV * (sum(state) + D[i]*state[i]), with D = `INTERNAL_DIAG_M1_16` and
R_INV = 943718400 = (2^32)^{-1} mod p. CONFIRMED: this is `p3-baby-bear`'s `DiffusionMatrixBabyBear`
as a canonical map (the R^{-1} factor is a Montgomery-form artifact, recovered exactly from the
library's 16x16 canonical matrix: off-diagonal = R^{-1}, diagonal = R^{-1}*(1 + D[i])). The textbook
form `state[i]*D[i] + sum` (a prior pass) is self-consistent but WRONG (the two-wrong-copies trap).
- Round constants: 8 * 16 external + 13 internal = 141 BabyBear elements. CONFIRMED: extracted from
the pinned crates (generated by `Xoroshiro128Plus::seed_from_u64(1)` via `new_from_rng_128`). THEY
ARE NOT INVENTED HERE; they are copied byte-exact from the pinned reference.
Interface.
- `poseidon2_permute(state: [Fp; 16]) -> [Fp; 16]` (the raw permutation).
- `poseidon2_compress(l: [Fp; 8], r: [Fp; 8]) -> [Fp; 8]` (the 2-to-1 Merkle compression; typically
a truncated permutation or a sponge, `[VERIFY]` which Plonky3 `TruncatedPermutation` shape SP1
uses).
State (IMPLEMENTED + CONFORMANCE CONFIRMED, 2026-07-19; real KAT PASSES). The permutation is
implemented at width 16: the x^7 S-box, the 8 external rounds with the M4-based MDS-light layer, the
13 internal rounds with the R^{-1}*(J+diag(D)) diagonal layer, the initial external linear layer, and
the 141-constant round schedule (external add to all 16 lanes, internal add to lane 0). It lives in
`Poseidon2Bb.permute` in the precompile (with `compress2to1`) and in three independent references:
`pqc-fork/pq-stark/poseidon2_babybear_reference.py`, `.mjs` (Node), and
`Poseidon2BabyBearSelfTest.java` (standalone). Because the permutation is confirmed,
`Poseidon2Bb.available` is now `true`. This does NOT make ACCEPT reachable: the duplex-sponge
challenger is a separate un-ported component (`Challenger.spongePorted = false`), and the constraint
eval and FRI are un-ported (`StarkConstraints` = UNAVAILABLE), so the top level returns EMPTY for
every input.
CONFORMANCE CONFIRMED (`pqc-fork/pq-stark/test_poseidon2_babybear.py`, ran 2026-07-19, PASS=47017
FAIL=0, Python + Node + Java byte-identical; standalone `Poseidon2BabyBearSelfTest` PASS=13536 FAIL=0).
The pinned crates `p3-poseidon2` / `p3-baby-bear` at crates.io version `0.4.3-succinct` (checksums
`522986377b2164c5f94f2dae88e0e0a3d169cc6239202ef4aeb4322d60feffd0` /
`d69e6e9af4eaaaa60f7bb9f0e0f73ebcbaefe7e00974d97ad0fa542d6a4f0890`, byte-identical to the repo's
`zk-circuits/*/Cargo.lock` and `rollup-evm-validity/*/Cargo.lock`) were executed via cargo to emit the
constants and three permutation known-answer vectors. The reference reproduces all three EXACTLY:
- KAT `zeros` (input [0;16]) -> [1787823396, 953829438, 89382455, ...] (16 values);
- KAT `iota` (input [0..15]) -> [157639285, 1851003038, 1852457045, ...];
- KAT `testvec`(p3 test input) -> [512585766, 975869435, 1921378527, ...].
Full vectors + source URLs are recorded in `pqc-fork/pq-stark/spec-poseidon2-constants.md`. Additional
self-consistency (also passing): x^7 bijection (7 = smallest coprime degree; inverse round-trips), the
permutation is a genuine bijection (inverse round-trips + 0 collisions), M4 MDS, both linear layers
invertible, and three-language byte-identity.
CONFIRMED items (all previously `[VERIFY]`, now closed by the real KAT):
- The 141 ROUND CONSTANTS are the CONFIRMED `Xoroshiro128Plus::seed_from_u64(1)` / `new_from_rng_128`
values, extracted (canonical) from the pinned crates. A prior pass used SHA-256 PLACEHOLDERS; those
are replaced.
- `INTERNAL_DIAG_M1_16` = [p-2, 1, 2, 4, 8, ..., 8192, 32768] CONFIRMED (canonical) against
`p3-baby-bear`'s `POSEIDON2_INTERNAL_MATRIX_DIAG_16_BABYBEAR_MONTY`. The Montgomery subtlety is
resolved: the internal LAYER carries a canonical R^{-1} factor (see the internal-linear-layer bullet
above), which a prior pass missed.
- `ROUNDS_P` = 13, `ROUNDS_F` = 8 CONFIRMED (`poseidon2_round_numbers_128(16, 7)` = `(8, 13)`).
- M4 and the MDS-light M_E CONFIRMED (recovered byte-exact from `Poseidon2ExternalMatrixGeneral`).
Remaining `[VERIFY]` for this component: only the 2-to-1 Merkle compression convention (component (c)):
which 8 output lanes / padding / rate SP1's `TruncatedPermutation` uses for `compress2to1`.
Hardest part (now done). Getting all 141 round constants, the internal diagonal, the internal-layer
Montgomery R^{-1} factor, and M4 byte-exact. This was the classic footgun (two implementations sharing
wrong constants agree with each other but not Plonky3); it is closed by executing the pinned crates and
diffing the real known-answer vectors, which cannot be faked.
Test vectors.
- Structural, self-checkable offline: PASSING (see the State paragraph): x^7 bijection, M4 MDS,
external/internal layers invertible, permutation inverse round-trip, cross-language agreement.
- Conformance (the real gate): PASSED. The pinned `p3-baby-bear` / `p3-poseidon2` `0.4.3-succinct`
crates were executed to produce known-answer outputs for fixed inputs (all-zero, [0..15], the p3
width-16 test vector); the port reproduces them byte-for-byte. Recorded in
`pqc-fork/pq-stark/spec-poseidon2-constants.md` and `../results/kat-results-poseidon2-babybear.json`.
Effort. Constants + internal-layer conformance: DONE. Remaining for a full component (c)/(d)/(e)/(f)
port is the sponge transcript, Merkle/MMCS, FRI fold arithmetic, and the recursion AIR.
## 4. Component (c): Merkle / MMCS vector-commitment scheme (IMPLEMENTED + CONFORMANCE-CONFIRMED)
What it does. The commitment layer FRI (d) and the trace commitment are built on: a Merkle tree
(Plonky3 `FieldMerkleTreeMmcs`) whose leaves are rows of field elements and whose internal nodes are
Poseidon2 compressions, supporting batched, mixed-height openings (the "mixed matrix commitment
scheme", MMCS). The verifier checks that a claimed opening (a row of values plus an authentication
path) hashes up to a committed root.
State (IMPLEMENTED + CONFORMANCE CONFIRMED, 2026-07-19; real KAT PASSES). The MMCS is implemented as
the EXACT SP1 inner config, confirmed verbatim from `p3-merkle-tree`'s own `mmcs.rs` tests:
- leaf hasher `PaddingFreeSponge<Perm, WIDTH=16, RATE=8, OUT=8>` (overwrite-mode, padding-free sponge);
- 2-to-1 node compressor `TruncatedPermutation<Perm, N=2, CHUNK=8, WIDTH=16>` (i.e.
`compress([l,r]) = permute(l||r)[0..8]`, the `Poseidon2Bb.compress2to1` already present);
- `FieldMerkleTreeMmcs<Packing, Packing, MyHash, MyCompress, DIGEST_ELEMS=8>`, so a digest is 8
BabyBear elements (32 bytes).
It lives in `Mmcs` in the precompile (`hashIter`, `compress2to1`, `verifyBatch`) and in three
independent references: `pqc-fork/pq-stark/mmcs_babybear_reference.py`, `.mjs` (Node), and
`MmcsBabyBearSelfTest.java` (standalone, which also builds/opens the tree for testing). Because the
construction and outputs are confirmed, `Mmcs.available` is now `true`. This does NOT make ACCEPT
reachable: the duplex-sponge challenger (`Challenger.spongePorted = false`, component (f)), the FRI
fold arithmetic (component (d)), and the recursion AIR (`StarkConstraints` = UNAVAILABLE, component
(e)) are un-ported, so the top level returns EMPTY for every input.
Interface.
- `verify_batch(root: Digest, dimensions: [(width, height)], index: usize, opened_rows: [[Fp]],
proof: [Digest]) -> bool`: recompute the root from the opened leaf rows and the sibling digests
along the path, compare to `root`. This is the ONLY method the on-chain verifier needs; the tree
build (`commit`) and `open_batch` are prover-side and live only in the offline references.
Dependencies. Poseidon2 (b) for the leaf hash and node compression, field (a) for the leaf encoding.
CONFORMANCE CONFIRMED (`pqc-fork/pq-stark/test_mmcs_babybear.py`, ran 2026-07-19, PASS=300 FAIL=0,
Python + Node + Java byte-identical; standalone `MmcsBabyBearSelfTest` PASS=80 FAIL=0). The pinned
crates `p3-merkle-tree` / `p3-symmetric` / `p3-commit` / `p3-matrix` at crates.io version
`0.4.3-succinct` (checksums `d5703d92...` / `9047ce85...` / `50acacc7...` / `75c3f150...`,
byte-identical to the repo Cargo.lock) were executed via cargo (extractor) to commit to six known
matrix batches and emit the root plus an `open_batch` opening (opened rows + sibling path) for each;
the extractor also asserts the library's OWN `verify_batch` accepts them. This reference reproduces
every root, opening, and proof EXACTLY (canonical u32), and its `verify_batch` accepts the emitted
openings and rejects tampered ones. The six cases exercise: single power-of-two height, single
non-power-of-two height (zero-digest padding), a column vector, and three mixed-height batches
(including one whose proof contains a default zero-digest sibling). Full vectors + source URLs are in
`pqc-fork/pq-stark/spec-mmcs-babybear.md`. A perm-sanity KAT (the extractor's `permute([0;16])`) equals
the confirmed Poseidon2 "zeros" vector, proving the extractor uses the same confirmed permutation.
CONFIRMED items (all previously `[VERIFY]`, now closed by the real KAT).
- The leaf-hashing convention (a row of width-w elements is absorbed by the overwrite-mode
PaddingFreeSponge, RATE 8, over the concatenation of all matrices at that height) CONFIRMED.
- The mixed-height batching rule (tallest-first, pad each layer to a power of two with the zero
digest, inject shorter matrices via `compress([node, hash(rows)])` at the layer whose padded length
matches, index reduced by `index >> (log_max_height - log2_ceil(height))`) CONFIRMED.
- The digest width (8 BabyBear elements = 32 bytes) and the `TruncatedPermutation` compression shape
(permute(l||r) truncated to 8 lanes) CONFIRMED.
Multi-matrix generalization. The references and `Mmcs.verifyBatch` handle the full mixed-height MMCS,
not just the single-matrix case: matrices are grouped tallest-first by padded height, the running root
is seeded with the tallest group's hashed concatenated rows, and each shorter group is compressed in at
the proof layer whose padded length matches its `next_power_of_two` height (Plonky3's height property
guarantees every padded bucket holds one exact height). The KAT includes three genuine mixed batches
(2 and 3 matrices, powers-of-two and non-powers-of-two), so this path is exercised, not just documented.
Remaining `[MEASURE]` for this component: conformance against a REAL exported SP1 v6.1.0 commitment
root (the trace / FRI matrices from an actual proof), which needs an exported proof and the challenger.
The construction is confirmed; matching a real proof's root is the end-to-end integration check.
Effort. Confirmed and implemented in this pass. The batching / mixed-height indexing was the fiddly
part and is now traced against the pinned source and KAT-locked.
## 5. Component (d): FRI folding, query-index derivation, low-degree test
What it does. FRI (Fast Reed-Solomon IOP of Proximity) is the polynomial-commitment / low-degree
test at the heart of the STARK. The verifier:
1. observes the commit-phase Merkle roots, sampling a folding challenge beta_i in F_{p^4} per round;
2. observes the final polynomial and checks the grinding (proof-of-work) witness;
3. derives `num_queries` query indices from the transcript (the query-index derivation);
4. for each query, walks the folding: opens the sibling pair at each layer against that layer's
commitment (component c), checks the fold relation p_{i+1}(x^2) = fold(p_i, beta_i) at the query
point, and confirms the final value matches the committed final polynomial.
State (IMPLEMENTED + CONFORMANCE CONFIRMED, 2026-07-19; real KAT PASSES for the FOLD + OPENING
relation). Two slices are now real: (1) the query-index derivation and per-layer folding-index walk
(section 8, `FriQueryIndex`), and (2) the full per-query FOLD + OPENING check `verify_query` (this
section), traced verbatim from the pinned `p3-fri` `verifier.rs` and CONFORMANCE-CONFIRMED against a
real FRI proof emitted by the pinned `p3-fri` prover. It lives in `Fri.verifyQuery` in the precompile
(`Fri.foldRelationConfirmed = true`) and in three independent references
(`pqc-fork/pq-stark/fri_verify_reference.{py,mjs}`, `FriVerifySelfTest.java`; harness
`test_fri_verify.py`, PASS=55 FAIL=0). The fold ARITHMETIC (interpolate the two sibling evaluations
through beta at the coset point, in F_{p^4}), the coset-point / bit-reversal mapping, and the
per-layer MMCS opening (component (c)) are all now IMPLEMENTED, not delegated. The transcript BINDING
that derives the betas / query indices / grinding IN-CIRCUIT from the Poseidon2 sponge (component (f))
is now ALSO confirmed (section 7): its derived betas/indices are re-verified end to end against this
section's `verify_query` (transcript -> betas/indices -> FRI verify, PASS in `test_challenger.py`), so
this section's KAT no longer needs the betas/indices as external inputs. What is still NOT ported is the
AIR reduced-opening combination (component (e)). So even with the fold+opening AND the transcript
confirmed, the top level stays FAIL-CLOSED (`Fri.checkQuery` returns UNAVAILABLE; the AIR
`StarkConstraints.evaluateAtZeta` returns UNAVAILABLE before the query loop).
Interface.
- `derive_query_indices(challenger, num_queries, log_max_height) -> [usize; num_queries]` (section 8:
implemented as `FriQueryIndex.sampleBits` + the sampling loop; the challenger squeeze is component (f)).
- `query_fold_walk(index, log_max_height, log_final_poly_len) -> [FoldStep]` where each `FoldStep`
gives (index_in_layer, sibling = index ^ 1, index_pair = index >> 1, parity) (section 8:
implemented as `FriQueryIndex.walk`).
- `verify_query(log_blowup, log_max_height, commits, betas, index, reduced_openings, sibling_values,
opening_proofs) -> Fp4|null` (IMPLEMENTED + CONFIRMED): walk the arity-2 folding, MMCS-open the
sibling pair per layer (component (c)), interpolate the pair through beta at the coset point x =
two_adic_generator(log_max_height)^reverse_bits_len(index) (the sibling point is -x), fold down to a
single F_{p^4} constant. `verify_challenges` checks that constant equals `final_poly` for every query.
betas / index / reduced_openings are INPUTS (transcript = component (f), AIR = component (e)).
CONFIRMED (all previously `[VERIFY]`, now closed by the real KAT vs pinned p3-fri 0.4.3-succinct).
- Arity-2 folding with `index_sibling = index ^ 1`, `index_pair = index >> 1`. CONFIRMED against
`p3-fri` `verifier.rs`.
- `log_max_height = commit_phase_commits.len() + log_blowup`; `num_fold_rounds = log_max_height -
log_blowup`; the final polynomial is a single F_{p^4} CONSTANT (log_final_poly_len = 0). CONFIRMED.
- The coset point x = `two_adic_generator(log_max_height)^reverse_bits_len(index, log_max_height)`, in
F_{p^4}; the sibling point is `x * two_adic_generator(1) = -x`. CONFIRMED (bit-reversal mapping).
- `two_adic_generator(bits)` = Plonky3's stored value (the fold coset points come out right only if it
matches). CONFIRMED. The F_{p^4} non-residue `W = 11` is Plonky3's (the EF fold reproduces the
interpolation). CONFIRMED.
- The commit-phase MMCS is `ExtensionMmcs<Val,Challenge,ValMmcs>`: a leaf is a PAIR of F_{p^4} evals
flattened to 8 BabyBear coords, opened by the CONFIRMED base FieldMerkleTreeMmcs (component (c)).
CONFIRMED.
`[VERIFY]` / `[MEASURE]` items still open.
- `sample_bits` LSB masking (section 8) and the transcript observe/sample ORDER are component (f), now
CONFIRMED (section 7): the betas/indices are derived in-circuit from the transcript and re-verified
end to end against this section's `verify_query` (transcript -> betas/indices -> FRI verify). The
betas/indices are no longer taken as inputs in the closed-loop KAT.
- The proximity parameters (num_queries, log_blowup, pow_bits) and the resulting soundness. The
skeleton placeholders are {100, 1, 16} (inner) and {25, 4, 16} (wrap) and MUST be replaced by the
frozen SP1 config, then scored by ethSTARK soundcalc (design doc section 4). `[MEASURE]`.
Test vectors.
- Implemented and validated (section 8): the query-index derivation and folding-index walk.
- CONFORMANCE (the real gate for the fold+opening): PASSED. The pinned `p3-fri` 0.4.3-succinct crate was
executed (`pqc-fork/pq-stark/fri-extractor`) to emit real FRI proofs over known low-degree
polynomials (with the confirmed `seed_from_u64(1)` Poseidon2), confirmed accepted by the library's own
`verify_challenges`; the references + `Fri.verifyQuery` reproduce the ACCEPT byte-for-byte and REJECT
four tamper variants (corrupted opened value, corrupted MMCS sibling, wrong beta/fold, non-matching
final poly). Recorded in `pqc-fork/pq-stark/spec-fri-babybear.md`,
`../results/kat-results-fri-verify.json`, and `fri_ground_truth.json`.
- `[MEASURE]` remaining: the actual query indices and fold values for a REAL exported SP1 v6.1.0 inner
proof (needs the Poseidon2 challenger, component (f), and the AIR reduced openings, component (e)).
Effort. The index logic (section 8) and the fold + opening relation (this section, `verify_query`) are
DONE and conformance-confirmed in this pass: the fold arithmetic, the coset/bit-reversal mapping, and
the per-layer MMCS opening all match the pinned p3-fri. What remains for a full component (d) in a real
verifier is only its INPUTS: the proximity-parameter pinning against the frozen SP1 config (`[MEASURE]`)
and the transcript that derives the betas/indices (component (f)) plus the AIR reduced openings
(component (e)). Those are separate components; until they are ported the top level stays fail-closed.
## 6. Component (e): AIR constraint evaluation and quotient consistency (GENERIC mechanism now IMPLEMENTED + CONFORMANCE-CONFIRMED; SP1-recursion-specific AIR + vkey remain [MEASURE])
What it does. The STARK proper. It checks that the committed trace satisfies the AIR (Algebraic
Intermediate Representation): evaluate every transition, boundary, and public-value constraint at the
DEEP out-of-domain point zeta, combine them with a random challenge alpha into a single folded
constraint value, and check that the quotient the prover committed is consistent (the DEEP-ALI /
quotient consistency check). For SP1 it also binds the proof to the specific program by checking the
verification-key digest and the public values.
This component SPLITS into two parts, and the split IS the fail-closed gate:
- (1) the GENERIC quotient-consistency MECHANISM (AIR-agnostic; works for ANY AIR): now IMPLEMENTED +
CONFORMANCE-CONFIRMED (2026-07-19). This is the tractable, ground-truth-confirmable piece.
- (2) the SPECIFIC SP1 recursion AIR (its exact constraint set + interactions + public-value layout)
and the vkey digest that commits to it: a large, program-specific, multi-week port needing the SP1
toolchain. UN-PORTED. This is what keeps the top level FAIL-CLOSED for real SP1 proofs.
### 6.1 The GENERIC quotient-consistency check (IMPLEMENTED + CONFORMANCE-CONFIRMED)
The exact generic identity a `p3-uni-stark` verifier checks, traced verbatim from the pinned
`p3-uni-stark` 0.4.3-succinct `verifier.rs` (lines 90-141), `p3-commit` `domain.rs`
(`TwoAdicMultiplicativeCoset` selectors), and `p3-air` `folder.rs`/`air.rs`
(`VerifierConstraintFolder`):
1. Trace domain = `TwoAdicMultiplicativeCoset { log_n = degree_bits, shift = 1 }`
(`TwoAdicFriPcs::natural_domain_for_degree`). g = `two_adic_generator(degree_bits)`.
2. `log_quotient_degree = log2_ceil(max_constraint_degree - 1)`; `quotient_degree = 1 << log_quotient_degree`.
3. Quotient domain = `create_disjoint_domain`: `log_n = degree_bits + log_quotient_degree`,
`shift = trace.shift * Val::generator() = GENERATOR` (= 31, CONFIRMED via the extractor's
`val_generator`). Chunk domain i (`split_domains`): `log_n = degree_bits`,
`shift = GENERATOR * two_adic_generator(degree_bits + log_quotient_degree)^i`.
4. Selectors at zeta (`selectors_at_point`, trace domain, shift = 1):
`z_h = zeta^(2^degree_bits) - 1`; `is_first_row = z_h / (zeta - 1)`;
`is_last_row = z_h / (zeta - g^-1)`; `is_transition = zeta - g^-1`; `inv_zeroifier = z_h^-1`.
5. Quotient reconstruction (`verifier.rs` lines 90-116):
`zps[i] = prod_{j != i} zp_j(zeta) * zp_j(chunk_i.first_point())^-1`, where
`zp_D(pt) = (pt * shift_D^-1)^(2^log_n_D) - 1`; then
`quotient(zeta) = sum_i zps[i] * sum_e monomial(e) * quotient_chunks[i][e]`, with `monomial(e) = x^e`
in F_{p^4} and `quotient_chunks[i]` the D=4 opened F_{p^4} coordinates of chunk i.
6. Constraint fold (`VerifierConstraintFolder`, Horner): `acc = acc * alpha + constraint`, over the AIR
`eval()` emission order; `when_first_row`/`when_transition`/`when_last_row` multiply the constraint by
`is_first_row`/`is_transition`/`is_last_row` (`p3-air` `air.rs` `FilteredAirBuilder`). The trace
openings `trace_local` (at zeta) and `trace_next` (at g*zeta) are the two AIR rows.
7. The identity: `folded_constraints * inv_zeroifier == quotient`, i.e.
`folded_constraints(zeta) == Z_H(zeta) * quotient(zeta)`; `OodEvaluationMismatch` otherwise
(`verifier.rs` lines 137-141).
Interface (implemented).
- `checkGenericQuotient(degree_bits, alpha, zeta, public_values, trace_local, trace_next,
quotient_chunks, air_id) -> bool`: the identity check above, for a SUPPLIED example AIR constraint
evaluator (`air_id`). Lives in `StarkConstraints.checkGenericQuotient` in the precompile
(`StarkConstraints.GENERIC_QUOTIENT_CHECK_CONFIRMED = true`) plus three independent references
(`pq-stark/air_quotient_reference.{py,mjs}`, `AirQuotientSelfTest.java`).
- Still NOT on the real path: `bind_vk(vkey_digest, preprocessed_commitment)` and the SP1 recursion
AIR constraint evaluator (part 2 below).
CONFORMANCE CONFIRMED (`pq-stark/test_air_quotient.py`, ran 2026-07-19, PASS=18 FAIL=0, Python + Node +
standalone Java byte-identical). The pinned crates `p3-uni-stark` / `p3-air` / `p3-baby-bear` /
`p3-commit` 0.4.3-succinct (checksums `fc3dfdeb...` / `d3a5de20...` / `d69e6e9a...` / `50acacc7...`, the
last two byte-identical to the Poseidon2/MMCS components) were executed via cargo
(`pq-stark/airquotient-extractor`) to run `p3_uni_stark::prove` then `p3_uni_stark::verify` on two KNOWN
example AIRs and confirm the LIBRARY accepts, then emit the openings + alpha (sample_ext) + zeta (sample)
by replaying the verifier's exact transcript prefix:
- `fibonacci_n8`: the Fibonacci AIR verbatim from `p3-uni-stark`'s own `tests/fib_air.rs` (n = 8,
public values `[0, 1, 21]`), which has ONE quotient chunk (log_quotient_degree = 0);
- `mul_deg3_n16`: a degree-3 multiply AIR matching `tests/mul_air.rs` (columns [a,b,c], constraint
`a^2*b - c` plus a boundary `b = a^2 + 1` and a transition `next_a = a + 1`, n = 16), which has TWO
quotient chunks (log_quotient_degree = 1), so it exercises the split-domain `zps` product.
The generic reference reproduces the library ACCEPT on both (the identity holds) and REJECTS three tamper
variants per case: a corrupted trace opening, a wrong quotient chunk, a wrong alpha. Full vectors are in
`pq-stark/air_quotient_ground_truth.json` and `../results/kat-results-air-quotient.json`. The extractor's
`perm_zeros` equals the confirmed Poseidon2 "zeros" vector and its `val_generator` equals 31, tying the
ground truth to the confirmed sub-components.
### 6.2 The SPECIFIC SP1 recursion AIR + vkey binding (UN-PORTED, [MEASURE], the fail-closed gate)
Hardest part, and the hardest component overall. The GENERIC mechanism (6.1) working on a small example
AIR does NOT let the precompile verify a real SP1 proof: the SP1 recursion machine AIR is not a short
formula. It is the entire `sp1-recursion-core` / `sp1-stark` symbolic constraint system (many columns,
selectors, the multiset/permutation interaction argument that links chips, and public-value bindings),
plus the vkey digest that commits to it. Porting it means reproducing the exact constraint polynomials,
the challenge derivation order, the interaction/permutation argument, and the DEEP quotient combination,
all byte-consistent with `StarkVerifier::verify_shard`. A single off-by-one in a selector or a
mis-ordered challenge makes the whole thing silently wrong.
`[MEASURE]` / `[VERIFY]` items (part 2, un-ported). The full recursion AIR (columns, constraints,
interactions); the public-values layout and binding; the vkey_digest domain (`vk.hash_babybear()`
packed). All against `sp1-stark` at the pinned v6.1.0 revision, and only a real exported SP1 inner proof
exercises them end to end.
Fail-closed gate. In the precompile, `StarkConstraints.SP1_RECURSION_AIR_PORTED = false`. While it is
false, `StarkConstraints.evaluateAtZeta` (the REAL verify path) returns UNAVAILABLE at stage 4 (before
the query loop and before the single `return ACCEPT`), so `computePrecompile` returns EMPTY for a real
SP1 proof and ACCEPT is unreachable. This is VERIFIED: a runtime check drives the real precompile classes
(the generic check accepts the ground truth + rejects tampers; `evaluateAtZeta(null,null) == UNAVAILABLE`;
`computePrecompile` on a well-formed AS1 wire input returns EMPTY).
Test vectors (end-to-end, part 2). Only real, exported SP1 inner proofs exercise this. `[MEASURE]`
end-to-end: ACCEPT a valid exported shrink/wrap proof, REJECT one with a mutated public value or a
flipped trace opening. This also needs the `WireReader` proof-body parser and the whole stack wired.
Effort. Part 1 (the generic mechanism): DONE + conformance-confirmed in this pass (~0.5 person-week of
the estimate). Part 2 (the SP1 recursion AIR + vkey + end-to-end KAT): about 3 to 4 person-weeks, the
largest single item, and the one most in need of a specialist STARK review. This is where "multi-week
port" is most literally true.
## 7. Component (f): Fiat-Shamir transcript (Poseidon2 duplex challenger) (IMPLEMENTED + CONFORMANCE-CONFIRMED)
What it does. The non-interactive transcript. A duplex sponge over Poseidon2 that absorbs
("observe") the vkey digest, public values, and every commitment in a FIXED order, and squeezes
("sample") the verifier randomness (folding challenges, alpha, zeta, query indices, grinding
challenge). If the observe/sample order does not match the prover exactly, every derived challenge
diverges and nothing verifies.
State (IMPLEMENTED + CONFORMANCE CONFIRMED, 2026-07-19; real KAT PASSES). The challenger is the EXACT
SP1 inner `DuplexChallenger<BabyBear, Perm, WIDTH=16, RATE=8>` plus the `GrindingChallenger`
`check_witness`, traced verbatim from the pinned `p3-challenger` 0.4.3-succinct source
(`duplex_challenger.rs`, `grinding_challenger.rs`) over the CONFIRMED Poseidon2 permutation (component
(b)). It lives in `Challenger` in the precompile (`Challenger.spongePorted = true`) and in three
independent references: `pqc-fork/pq-stark/challenger_reference.py`, `.mjs` (Node), and
`ChallengerSelfTest.java` (standalone). CONFIRMED behaviour, all from source:
- `observe(felt)`: overwrite-mode absorb; clears the output buffer, pushes to the input buffer, and
duplexes (permutes) once the input buffer reaches RATE=8. Digests/ext elements are observed
element-by-element (`observe_ext_element` = observe the 4 base coords in order).
- `duplexing`: overwrite the first `input_buffer.len()` (<= RATE) lanes of the 16-lane state with the
buffered inputs, permute, then set the output buffer to state lanes `[0..RATE)`.
- `sample() -> Fp` / `sample() -> Fp4`: duplex if inputs are buffered OR the output buffer is empty,
then `pop()` from the END of the output buffer (`Vec::pop`); an Fp4 sample is 4 sequential base
pops `[c0, c1, c2, c3]` (`sample_ext_element` = `sample_vec(4)` then `from_base_slice`).
- `sample_bits(bits)`: the LOW `bits` bits of a base sample's `as_canonical_u64` (CONFIRMED LSB, not
MSB). This is the query-index reduction (section 8).
- `check_witness(pow_bits, witness)`: `observe(witness)` then `sample_bits(pow_bits) == 0` (grinding).
Interface.
- `observe(felts: [Fp])`, `observe_digest(d: Digest)`, `sample() -> Fp4`, `sample_bits(bits) ->
usize`, `check_witness(pow_bits, witness) -> bool` (grinding).
Dependencies. Poseidon2 (b). This is a thin, mostly-mechanical layer on top of the permutation, but
its ORDER is a strict, pervasive contract shared with components (d) and (e).
CONFORMANCE CONFIRMED (`pqc-fork/pq-stark/test_challenger.py`, ran 2026-07-19, PASS=44 FAIL=0, Python +
Node + standalone Java byte-identical; standalone `ChallengerSelfTest` PASS=31). The pinned crate
`p3-challenger` 0.4.3-succinct (the exact challenger `DuplexChallenger<Val, Perm, 16, 8>` +
`GrindingChallenger`), with the transcript ORDER taken from `p3-fri` 0.4.3-succinct
`verify_shape_and_sample_challenges`, was executed via cargo (`pqc-fork/pq-stark/challenger-extractor`,
byte-identical lockfile) to emit two ground-truth blocks, both reproduced EXACTLY by the references:
- a fully-scripted observe/sample transcript: base samples, F_{p^4} ext samples, `sample_bits`, the
full 16-lane sponge state at a checkpoint, and a `check_witness` accept/reject table (a real grinding
witness accepts, fixed non-witnesses reject) all match byte-for-byte;
- the FRI transcript closure: for the SAME three FRI cases the component (d) ground truth uses, the
betas + query indices + grinding acceptance are DERIVED from the transcript (commit-phase commits +
final poly + pow witness) and match BOTH the pinned p3-fri challenger AND `fri_ground_truth.json`.
Fed into the CONFIRMED (d) `verify_query`, the derived betas/indices ACCEPT the real FRI proof and
REJECT a tampered beta. This CLOSES the loop (d) left open (transcript -> betas/indices -> FRI verify);
the betas/indices are now derived in-circuit, not taken as inputs. Recorded in
`pqc-fork/results/kat-results-challenger.json` and `pqc-fork/pq-stark/challenger_ground_truth.json`.
CONFIRMED items (all previously `[VERIFY]`, now closed by the real KAT).
- The observe/sample SEQUENCE (`verify_shape_and_sample_challenges`): per commit-phase commit, observe
the 8-element digest then sample one beta in F_{p^4}; then observe the final polynomial (4 base
coords); then `check_witness(pow_bits, pow_witness)`; then `num_queries` x `sample_bits(log_max_height)`.
- The sponge RATE/CAPACITY split for width 16 (RATE=8, capacity 8, overwrite-mode absorb) and the
Vec::pop LIFO squeeze order.
- The grinding convention (`check_witness` = observe then `sample_bits == 0`).
Remaining `[MEASURE]` for this component: the transcript of a REAL exported SP1 v6.1.0 proof (the exact
observe order of the vkey digest, trace/quotient commitments, opened values, and DEEP zeta/alpha
sampling for the AIR). That is part of component (e)'s integration and needs an exported proof.
Test vectors. Conformance (the real gate): PASSED (see the State paragraph). The transcript reproduces
the pinned p3-challenger / p3-fri sampled values byte-for-byte and closes the FRI betas/indices loop.
Effort. Confirmed and implemented in this pass. The observe/sample ordering and the Vec::pop squeeze
direction were the subtle parts (an order mismatch presents as "everything rejects" with no localized
error); both are now traced against the pinned source and KAT-locked.
## 8. The ONE sub-component implemented and tested in this pass: FRI query-index derivation
Rationale for the choice. Of the two candidates the task names (the Poseidon2 permutation, or the
FRI query-index derivation), the query-index derivation is the one that can be validated HONESTLY
with RUNNING, PASSING tests offline in a single pass, because it is determined entirely by a public
integer specification and depends on NO secret round constants. Poseidon2, by contrast, cannot be
conformance-validated without the exact Plonky3 constants (section 3); implementing it and testing it
only against a same-constants reference would prove self-consistency, not conformance, and would risk
exactly the "two wrong copies agree" trap. So Poseidon2 is left as a fully-specified `[VERIFY]` port
target (section 3) and the FRI query-index derivation is the piece implemented and tested here.
What is implemented (constant-free, spec-determined integer logic).
- `sample_bits(canonical_u32, bits)`: reduce a sampled BabyBear field element (its canonical u32
representative) to a query index in [0, 2^bits) by taking the low `bits` bits. This is the
transcript-to-index step of `derive_query_indices`.
- `walk(index, log_max_height, log_final_poly_len)`: the per-layer folding-index walk, producing for
each of the `num_fold_rounds = log_max_height - log_final_poly_len` FRI layers the tuple
(index_in_layer, sibling = index ^ 1, index_pair = index >> 1, parity = index & 1), and the
terminal `final_index = index >> num_fold_rounds` that must index the final polynomial's domain.
Where it lives.
- Production side (Java, behind the still-fail-closed top level): a new `FriQueryIndex` static helper
in `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java`, referenced from `Fri.checkQuery`
for the index-derivation step. Wiring it does NOT make ACCEPT reachable: the fold arithmetic and
Poseidon2 Merkle opening are still delegated, so `Fri.checkQuery` still returns UNAVAILABLE and the
driver still returns EMPTY. The top level stays fail-closed.
- Independent references and tests: `pqc-fork/pq-stark/fri_query_index_reference.py` (Python),
`pqc-fork/pq-stark/fri_query_index_reference.mjs` (Node, a genuinely different-language second
implementation), `pqc-fork/pq-stark/FriQueryIndexSelfTest.java` (a standalone copy of the exact
Java helper logic, compilable and runnable without the Besu classpath), and the harness
`pqc-fork/pq-stark/test_fri_query_index.py`.
How it is validated (what actually ran).
1. Hand-computed golden vectors (worked out by hand in the harness comments and asserted).
2. Structural invariants over many random cases: sibling differs from index in exactly bit 0; each
layer halves the index; after `num_fold_rounds` layers the index collapses into the final-poly
domain ([0, 2^log_final_poly_len)); `sample_bits` output is always in range and equals the low
bits; full-width `sample_bits` is the identity.
3. Cross-language agreement: Python, Node, and Java implementations produce byte-identical walks and
`sample_bits` outputs over a shared vector set. Three independent implementations (two languages
plus a hand golden) agreeing validates the LOGIC against the public spec.
What this does and does NOT prove. It proves the index LOGIC matches the public Plonky3/FRI
specification and is internally consistent across three implementations. It does NOT prove
conformance to a real SP1 v6.1.0 trace, because the actual sampled indices come out of the Poseidon2
challenger on a real proof, which needs component (b) and an exported proof. That conformance is
`[MEASURE]`:
```
# [MEASURE] conformance of derive_query_indices against a real SP1 inner proof.
# On a throwaway non-infra prover box (see pqc-fork/pq-stark/export-inner-stark-vector.md):
# 1. export an SP1 v6.1.0 shrink proof + its FriConfig (num_queries, log_blowup, pow_bits),
# 2. instrument the Plonky3 p3-fri verifier to print the sampled query indices and each layer's
# (index, index_sibling, index_pair) for one query,
# 3. feed the same challenger transcript bytes into fri_query_index_reference and assert the
# derived indices and fold walks match byte-for-byte.
```
Test result of this pass: see section 8 of `pqc-fork/pq-stark/README.md` and the machine-readable
`pqc-fork/results/kat-results-fri-query-index.json`. Summary is recorded there and in the port
summary; the top-level verifier remains fail-closed and unchanged in behavior.
## 9. Total effort, hardest parts, and the honest multi-week statement
| component | effort (person-weeks) | risk | status |
|---|---|---|---|
| (a) field + F_{p^4} | ~0.5 | low | IMPLEMENTED + TESTED (2026-07-19); W pinned + proven-irreducible; `[VERIFY]` W/gen = Plonky3's |
| (b) Poseidon2-BabyBear | ~1.5 to 2 | medium (constants) | IMPLEMENTED + CONFORMANCE-CONFIRMED (2026-07-19, PASS=47017; real KAT vs p3-* 0.4.3-succinct, checksums matched) |
| (c) Merkle / MMCS | ~1 to 1.5 | medium | IMPLEMENTED + CONFORMANCE-CONFIRMED (2026-07-19, PASS=300; real KAT vs p3-merkle-tree/p3-symmetric/p3-commit 0.4.3-succinct); end-to-end vs a real proof root `[MEASURE]` |
| (d) FRI fold + query + LDT | ~2 to 3 | high | index derivation (section 8) + FOLD+OPENING verify_query (section 5) IMPLEMENTED + CONFORMANCE-CONFIRMED (2026-07-19, PASS=55; real KAT vs p3-fri 0.4.3-succinct: accept genuine + reject tampered). Remaining: proximity params `[MEASURE]`; betas/indices come from the transcript (f) |
| (e) AIR + quotient consistency (GENERIC mechanism) | ~0.5 | medium | IMPLEMENTED + CONFORMANCE-CONFIRMED (2026-07-19, PASS=18; real KAT vs p3-uni-stark/p3-air/p3-commit 0.4.3-succinct: the generic DEEP-ALI identity accepts two known example AIRs' proofs + rejects tampered) |
| (e) SP1-recursion-SPECIFIC AIR + vkey binding | ~3 to 4 | HARDEST | `[MEASURE]` port target (the SP1 recursion machine AIR + vkey; keeps the top level fail-closed for real SP1 proofs; needs the SP1 toolchain + a real end-to-end proof KAT) |
| (f) Fiat-Shamir transcript | ~0.5 to 1 | medium (pervasive) | IMPLEMENTED + CONFORMANCE-CONFIRMED (2026-07-19, PASS=44; real KAT vs p3-challenger/p3-fri 0.4.3-succinct: transcript reproduced byte-for-byte AND the derived FRI betas/indices close the loop into the confirmed (d) verify_query). Real SP1-proof transcript order `[MEASURE]` (part of (e)) |
| integration, gas bench, conformance corpus, audit prep | ~2 to 3 | high | not started |
| total | ~11 to 16 | | multi-week / multi-month |
Hardest parts, ranked: (1) the SP1-recursion-SPECIFIC AIR constraint evaluation + vkey binding (part 2
of component e, section 6.2), the single largest and most error-prone item, still un-ported and
`[MEASURE]` (the GENERIC quotient mechanism of component (e) is now done + confirmed, section 6.1); (2)
the FRI fold arithmetic and its coset/bit-reversal mapping plus proximity-parameter pinning (component
d); (3) transcribing the Poseidon2 constants exactly (component b). None of these is a weekend job. A
realistic single-specialist timeline is 3 to 4 months including the conformance corpus and audit
preparation, which is why the production path the design doc recommends (section 5.1) is to wrap a
pinned, reproducible native build of the maintained Rust references rather than hand-port all six
components into Java.
Whichever path is taken, the gate is unchanged: the top-level 0x0AE8 stays fail-closed until the
whole verifier is real, KAT-green against real vectors, soundcalc-scored, gas-benchmarked under
the EIP-7825 cap, externally audited, isolated-soaked, and founder-activated
(`docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md`). Nothing short of that verifies a real proof.
**Conclusion, scoped honestly (per the top caveat).** The six generic components (a-f, minus the
SP1-recursion-specific AIR) form a REAL, conformance-confirmed BabyBear + FRI STARK verifier
skeleton, validated byte-for-byte against the pinned Plonky3 `0.4.3-succinct` crates. That skeleton
verifies BabyBear + FRI STARKs, for example Aere's own Plonky3 zk-circuits. It does NOT verify SP1
6.1.0 proofs, and finishing the "SP1 recursion AIR" row above would NOT make it do so: SP1 6.1.0 is
Hypercube (KoalaBear multilinear, BaseFold + Jagged + sumcheck-zerocheck + LogUp-GKR), a different
proof system in which FRI and DEEP-ALI play no part. Replacing the BN254 Groth16 wrap on real SP1
proofs is therefore a separate ~22 to 32 person-week retarget and a founder decision, specified in
`AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md` and `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md`.
The effort table above (~11 to 16 person-weeks) is the cost of a BabyBear+FRI verifier, not of an SP1
6.1.0 verifier.
## 10. Files
- `docs/AERE-STARK-VERIFIER-PORT-SPEC.md` (this document).
- `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java` (fail-closed skeleton + the
COMPLETE `BabyBear` / `BabyBearExt4` field arithmetic from section 2, the `Poseidon2Bb` permutation
from section 3 (`available` true, conformance-confirmed), the `Mmcs` vector commitment from section 4
(`available` true, conformance-confirmed), the `FriQueryIndex` helper from section 8, the FRI
`Fri.verifyQuery` fold + opening relation from section 5 (`Fri.foldRelationConfirmed` true,
conformance-confirmed; `Fri.checkQuery` stays gated), the `Challenger` duplex sponge from section
7 (`Challenger.spongePorted` true, conformance-confirmed), and the `StarkConstraints.checkGenericQuotient`
generic AIR quotient-consistency mechanism from section 6.1 (`StarkConstraints.GENERIC_QUOTIENT_CHECK_CONFIRMED`
true, conformance-confirmed), all wired behind the fail-closed top level. The remaining un-ported
crypto-core piece is the SP1-recursion-SPECIFIC AIR + vkey binding (section 6.2): with
`StarkConstraints.SP1_RECURSION_AIR_PORTED = false`, `StarkConstraints.evaluateAtZeta` (the real path)
returns UNAVAILABLE at the constraint stage before the query loop and before the single `return
ACCEPT`, so `computePrecompile` still returns EMPTY for every real SP1 proof input).
- `pqc-fork/pq-stark/babybear_field_reference.py`, `babybear_field_reference.mjs`,
`BabyBearFieldSelfTest.java`, `test_babybear_field.py` (the section-2 field sub-component + tests).
- `pqc-fork/results/kat-results-babybear-field.json` (machine-readable field test result).
- `pqc-fork/pq-stark/poseidon2_babybear_reference.py`, `poseidon2_babybear_reference.mjs`,
`Poseidon2BabyBearSelfTest.java`, `test_poseidon2_babybear.py` (the section-3 Poseidon2 sub-component
+ tests; conformance to Plonky3 CONFIRMED via a real KAT, see `spec-poseidon2-constants.md`).
- `pqc-fork/results/kat-results-poseidon2-babybear.json` (machine-readable Poseidon2 test result).
- `pqc-fork/pq-stark/mmcs_babybear_reference.py`, `mmcs_babybear_reference.mjs`,
`MmcsBabyBearSelfTest.java`, `test_mmcs_babybear.py` (the section-4 MMCS sub-component + tests;
conformance to Plonky3 CONFIRMED via a real KAT, see `spec-mmcs-babybear.md`).
- `pqc-fork/results/kat-results-mmcs-babybear.json` (machine-readable MMCS test result).
- `pqc-fork/pq-stark/fri_query_index_reference.py`, `fri_query_index_reference.mjs`,
`FriQueryIndexSelfTest.java`, `test_fri_query_index.py` (the section-8 sub-component + tests).
- `pqc-fork/results/kat-results-fri-query-index.json` (machine-readable test result).
- `pqc-fork/pq-stark/fri_verify_reference.py`, `fri_verify_reference.mjs`, `FriVerifySelfTest.java`,
`test_fri_verify.py` (the section-5 FRI fold + opening verify_query sub-component + tests;
conformance to Plonky3 p3-fri CONFIRMED via a real KAT, see `spec-fri-babybear.md`).
- `pqc-fork/pq-stark/challenger_reference.py`, `challenger_reference.mjs`, `ChallengerSelfTest.java`,
`test_challenger.py` (the section-7 Fiat-Shamir duplex-challenger sub-component + tests; conformance
to Plonky3 p3-challenger / p3-fri CONFIRMED via a real KAT, and the FRI betas/indices loop closed).
- `pqc-fork/pq-stark/challenger-extractor/` (Cargo.toml + Cargo.lock + src/main.rs: the Rust
ground-truth extractor depending on the pinned p3-challenger / p3-fri 0.4.3-succinct crates) and
`pqc-fork/pq-stark/challenger_ground_truth.json` (its authoritative output: the scripted transcript
outputs + the FRI transcript closure with pow_witness, reproduced by the references).
- `pqc-fork/results/kat-results-challenger.json` (machine-readable challenger test result).
- `pqc-fork/pq-stark/fri-extractor/` (Cargo.toml + Cargo.lock + src/main.rs: the Rust ground-truth
extractor depending on the pinned p3-fri 0.4.3-succinct crates) and `pqc-fork/pq-stark/fri_ground_truth.json`
(its authoritative output: real FRI proofs re-verified by the references).
- `pqc-fork/results/kat-results-fri-verify.json` (machine-readable FRI verify test result).
- `pqc-fork/pq-stark/air_quotient_reference.py`, `air_quotient_reference.mjs`, `AirQuotientSelfTest.java`,
`test_air_quotient.py` (the section-6.1 GENERIC AIR quotient-consistency sub-component + tests;
conformance to Plonky3 p3-uni-stark CONFIRMED via a real KAT on the Fibonacci + degree-3 mul example AIRs).
- `pqc-fork/pq-stark/airquotient-extractor/` (Cargo.toml + Cargo.lock + src/main.rs: the Rust ground-truth
extractor depending on the pinned p3-uni-stark / p3-air / p3-baby-bear / p3-commit 0.4.3-succinct crates)
and `pqc-fork/pq-stark/air_quotient_ground_truth.json` (its authoritative output: real p3-uni-stark
proofs of the two example AIRs, with the openings + alpha + zeta the generic identity re-checks).
- `pqc-fork/results/kat-results-air-quotient.json` (machine-readable AIR quotient-consistency test result).
- `docs/PQ-STARK-VERIFIER-PRECOMPILE-2026-07-18.md`, `docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md`
(design + activation), `pqc-fork/pq-stark/export-inner-stark-vector.md` (real-vector export).

87
AERE-TLA-FORMAL-SPEC.md Normal file
View File

@ -0,0 +1,87 @@
# Spec 27: TLA+ specification of Aere QBFT consensus
Date: 2026-07-19
Deliverable location: `aerenew/formal-tla/` (new directory)
## What was built
A real, well-formed TLA+ model of Aere Network's QBFT / IBFT 2.0 consensus, with
model-checkable safety and liveness properties and TLC configs. It complements
(does not replace) the existing z3 / SMT proofs in `aerenew/formal-consensus/`,
establishing the same properties by a second, independent method.
Files:
| File | Role |
|---|---|
| `aerenew/formal-tla/QBFT.tla` | The specification (constants, vars, actions, properties). 66 definitions; single-height, bounded-round IBFT 2.0. |
| `aerenew/formal-tla/MCsafety.tla` | Concrete N=4 and N=7 instances + message-pool bound. |
| `aerenew/formal-tla/MCsafety.cfg` | Safety, N=4, F=1, Quorum=3, rounds {0,1,2}. |
| `aerenew/formal-tla/MCsafety7.cfg` | Safety, live N=7, F=2, Quorum=5, rounds {0,1}. |
| `aerenew/formal-tla/MCliveness.tla` | Liveness instance (N=4, round-0 proposer Byzantine). |
| `aerenew/formal-tla/MCliveness.cfg` | `EventualCommit` under `FairSpec` fairness. |
| `aerenew/formal-tla/README.md` | What it proves, how to run TLC, relation to z3, honest status. |
## Faithfulness to QBFT / IBFT 2.0
- Constants pin the Besu formulas via `ASSUME`: `Quorum = ceil(2N/3)`,
`F = floor((N-1)/3)`. N=7 gives quorum 5, f 2 (the live set).
- Four message types (PRE-PREPARE / PREPARE / COMMIT / ROUND-CHANGE) in a uniform
record pool; quorum accounting counts DISTINCT senders.
- IBFT 2.0 locking: `Justified(r,v)` requires a quorum of valid round-change
messages and the highest-prepared value among them, backed by a REAL prepare
quorum (`ValidRoundChange` rejects a fabricated high-prepared certificate,
because a genuine prepare quorum needs Q distinct senders and only F are
Byzantine). Honest `Prepare` is guarded by `Justified` (acceptor-side lock);
`RoundChange` carries the sender's prepare certificate.
- Up to F Byzantine validators unconstrained via `FaultyBroadcast` (inject any
message from their own identities; cannot forge honest signatures).
- Proposer takes an explicit self-`Prepare` (mirrors the 2026-07-14 fixed Besu
rule from `qbft_prepare_counting_smt.py`).
## Properties
- Safety invariants: `Agreement` (no two honest validators decide different
blocks) and `NoFork` (committed set never holds two distinct blocks for one
height). Mirror `qbft_safety_smt.py` L2/L3 and `qbft_locking_smt.py`.
- Liveness: `EventualCommit` under `FairSpec` (WF on each honest action, none on
Byzantine). `MCliveness` makes proposer(0) Byzantine so a commit requires a
round change to an honest proposer, exercising `qbft_liveness_smt.py`'s path.
## Relationship to z3 models
Same properties, different method. z3 proves each property's negation UNSAT
(including the UNBOUNDED all-N quorum-intersection lemma `2q-N >= f+1` and
`N-f >= quorum`, which finite TLC cannot do). TLC gives an independent
explicit-state check of the message-passing protocol at concrete sizes N in
{4,7}. Cross-method agreement is the credibility gain. The all-N arithmetic stays
the z3 models' job; TLC contributes the finite-state protocol exploration.
## Honesty flags
- **[VERIFY] SANY parse not run.** Structure self-checked by a heuristic script
(module headers + `====` terminators present; parens/brackets/braces/block
comments balanced; all referenced operators resolve to a definition / constant /
variable / `Integers`,`FiniteSets`,`Sequences` builtin / bound var). This is NOT
a real parser. A `tla2sany.SANY QBFT.tla` run on a box with the TLA+ tools jar
should confirm no residual syntax issue.
- **[MEASURE] No TLC run executed.** The TLA+ tools jar / Apalache are not
installed in this environment (Java 21 IS present, but that alone is not a TLA+
tool, and downloading a jar was out of scope). No property is claimed
model-checked. Run `tlc MCsafety -config MCsafety.cfg`, `tlc MCsafety -config
MCsafety7.cfg`, `tlc MCliveness -config MCliveness.cfg` and record
states-explored + no-violation / property-holds.
- **Bounded model check, not all-N.** Fixed finite instances; message-pool cap is
a knob. A decision procedure over the finite config, not a proof for all N.
- **Liveness is temporal, post-GST.** Progress only after GST; asynchronous
liveness impossible by FLP and not claimed. Bounded witness only; unbounded
liveness not decidable by finite TLC.
- **Scope.** Models agreement / progress design, not Besu Java bytecode. Chain
2800 consensus uses classical ECDSA seals (N=7, f=2, quorum 5). The agreement
model is orthogonal to the seal scheme; nothing implies post-quantum consensus.
## Status
Artifact written and structurally self-checked. Safety and liveness are
specified and structurally validated, NOT yet model-checked (needs the TLC run
above). Complements, does not replace, the z3 corpus.

61
AERE-VSM-SUMMARY.md Normal file
View File

@ -0,0 +1,61 @@
# Spec 12: Aere VSM (Verifiable Semantic Memory) — DONE
On-chain attestation for an AI agent's vector-store memory: clear owner, verifiable
append-only history, PQC-authenticated commits, and paid attested retrieval on the AERE402 rail.
## Deliverables (all real, tested)
1. Contract: `aerenew/contracts/contracts/agentic/AereVectorStore.sol` (solc 0.8.23, new file, no deployed contract modified)
2. Test: `aerenew/contracts/test/aere-vector-store.test.js` — 8 passing
3. Doc: `aerenew/docs/AERE-VSM.md`
## Contract surface
- `createStore(storeId, falconPubKey?, settlementToken?, queryPrice?, paymentRecipient?)`
Config FIXED at creation (no setters). Classical store (empty key, owner-signed) or
post-quantum store (897-byte Falcon-512 key, header 0x09). Fail-closed on malformed key /
inconsistent price config.
- `commit(storeId, vectorRoot, vectorCount, uri, falconSig)` -> version
Append-only versioned root. PQC store: falconSig verified in full via 0x0AE1 over a
per-version challenge (COMMIT_DOMAIN|chainid|this|storeId|version|root|count|keccak(uri));
invalid sig reverts PQCVerificationFailed (fail-closed). Classical store: msg.sender==owner.
Empty/zero precompile return (pre-fork) treated as INVALID. Reuses the exact Falcon-512 input
construction from AerePQCAttestation.sol.
- `attestRetrieval(storeId, version, queryHash, resultRoot, receiptId)` -> retrievalId
Version must exist (1..current) or revert UnknownVersion (fail-closed). Priced store: must
cite a matching unconsumed AERE402 receipt (PaymentRequired / ReceiptConsumed / ReceiptMismatch).
- `paidQuery(storeId, queryHash, agentId, amount, nonce, deadline, authSig)` -> receiptId
References existing AERE402Facilitator (NOT redeployed). Vector store is the payee, calls
facilitator.settle, forwards received tokens (balance delta) to paymentRecipient in-tx; no
cross-call custody. Mints a receipt bound to (store, query, agent, nonce).
- Inclusion views: `verifyVectorInclusion(storeId, version, leaf, proof)` (membership in the
committed vectorRoot) and `verifyResultInclusion(retrievalId, leaf, proof)` (membership in the
attested resultRoot). OpenZeppelin MerkleProof (sorted-pair keccak256).
- No admin, no owner override, no upgrade hook, no path rewrites a committed version.
## Real test result
`npx hardhat test test/aere-vector-store.test.js` from aerenew/contracts: 8 passing.
1. PQC commit v1 then v2, append-only history preserved
2. tampered Falcon sig rejected fail-closed (also wrong-version + wrong-key rejected)
3. empty precompile return (pre-fork) treated as invalid
4. retrieval attestation records vs valid version; missing/zero version reverts
5. inclusion proof: member verifies, non-member fails (vectorRoot + resultRoot)
6. AERE402 paid-query: real facilitator settle, fee to sink, remainder to recipient, receipt
gates attestRetrieval; unpaid reverts PaymentRequired, reuse ReceiptConsumed, wrong-query
ReceiptMismatch; store holds 0 across the call
7. classical store: only owner may commit (non-owner NotOwner)
8. createStore fail-closed on bad key / inconsistent config / duplicate storeId
Falcon MOCKED in test (MockPQCPrecompile at 0x0AE1 via hardhat_setCode); real encoding proven
separately vs LIVE mainnet precompile by AerePQCAttestation path.
## Honesty flags
- [VERIFY]: on-chain commit registry, PQC-authenticated commits (0x0AE1), retrieval attestation,
inclusion proofs, AERE402 paid-query gate — built + tested.
- [MEASURE]: embedding model, vector similarity search / index, raw vector data, and semantic
quality/relevance — all OFF-CHAIN. On-chain attestation proves PROVENANCE + OWNERSHIP + that a
retrieval named a committed version, NOT semantic quality/relevance.
- Scope boundary preserved: PQC-authenticated memory commits do NOT make consensus post-quantum
(blocks still Besu QBFT + classical ECDSA). No new token (AERE402 uses existing rails).
- No em-dashes / "--" in prose. Brand "Aere Network"/"Aere", ticker AERE uppercase.

250
AERE-VSM.md Normal file
View File

@ -0,0 +1,250 @@
# Aere VSM: Verifiable Semantic Memory for AI Agents
Status: on-chain registry, attested retrieval, and AERE402 paid query are BUILT and TESTED [VERIFY].
The embedding model, the vector similarity search, and the raw vector data are OFF-CHAIN [MEASURE].
Contract: `aerenew/contracts/contracts/agentic/AereVectorStore.sol` (solc 0.8.23).
Tests: `aerenew/contracts/test/aere-vector-store.test.js` (8 passing, Falcon mocked in test).
---
## 1. What this is
An AI agent's long-term memory is a vector store: text, decisions, and preferences are turned
into embeddings and indexed for nearest-neighbour retrieval. Today that memory has no clear
owner, no verifiable history, and no way for a consumer to check that a returned result was
really part of the memory the agent claims to hold. Switch provider and the memory dies with
the vendor.
Aere VSM gives an agent's vector store three on-chain properties it does not have anywhere else:
1. A clear OWNER, an account or an agent identity that the memory is bound to.
2. A verifiable, APPEND-ONLY HISTORY of committed memory states, each optionally authenticated
by a post-quantum Falcon-512 signature verified in full on-chain.
3. PAID, ATTESTED RETRIEVAL, so a retrieval provider can attest "for this query, against this
committed memory version, these are the results", metered through the existing AERE402
agentic payment rail, and a consumer can verify a returned vector was actually committed.
This is the "decentralized AI memory" idea anchored to Aere's live post-quantum precompiles and
its existing payment rail. It is deliberately honest about the boundary between what a chain can
prove (provenance, ownership, that a retrieval named a committed version) and what it cannot
(that the embeddings are semantically good).
---
## 2. Architecture
```
OFF-CHAIN [MEASURE] ON-CHAIN [VERIFY] (chain 2800)
------------------------------ ------------------------------------------
documents / events / prefs
|
v embedding model AereVectorStore.sol
vectors + metadata ------------------------------------------
| createStore(storeId, falconPubKey?, token?,
v build index + Merkle root price?, recipient?)
vectorRoot = Merkle/commitment -------> commit(storeId, vectorRoot, vectorCount,
over the vectors uri, falconSig)
| - post-quantum store: falconSig verified
v publish snapshot via 0x0AE1 (Falcon-512), fail-closed
uri (ipfs:// / https://) - classical store: msg.sender == owner
- append-only version history
query embedding paidQuery(storeId, queryHash, agentId, amount,
| nonce, deadline, authSig)
v similarity search (ANN) - settles on the AERE402 rail (references
candidate vectors -------------------> AERE402Facilitator, does not redeploy it)
| - mints a receipt bound to (store, query)
v Merkle proofs + resultRoot
result set attestRetrieval(storeId, version, queryHash,
resultRoot, receiptId)
- version must exist (fail-closed)
- priced store: consumes a paid receipt
- records the provider's attestation
consumer holds a returned vector -------> verifyVectorInclusion(storeId, version,
leaf, proof) -> membership in the
committed vectorRoot
verifyResultInclusion(retrievalId, leaf, proof)
-> membership in the attested resultRoot
```
The flow is: embed and index OFF-CHAIN, commit the root ON-CHAIN, attest retrieval ON-CHAIN,
pay for the query on the AERE402 rail, and let the consumer verify inclusion against the
committed root.
---
## 3. The on-chain object model
### Store
A store is created once and its configuration is FIXED at creation (no setter for the Falcon
key, the price, the settlement token, or the recipient). A store is either:
- CLASSICAL: no Falcon key. Commits require `msg.sender == owner`.
- POST-QUANTUM: a 897-byte Falcon-512 public key (header `0x09`). Commits require a Falcon
signature that verifies on-chain via the live precompile at `0x0AE1`. Authorship IS the
signature, so any relayer may submit the transaction and only the key holder can produce a
valid commit.
### Version (append-only)
Each `commit` appends a new `Version { vectorRoot, vectorCount, committedAt, blockNumber, uri }`.
Version numbers start at 1 and increase by exactly one. No function can overwrite or delete a
committed version. The current root and every past version stay queryable forever. There is no
admin, no owner override, and no upgrade hook, so there is nothing that can rewrite a committed
root.
### Retrieval attestation
`attestRetrieval(storeId, version, queryHash, resultRoot, receiptId)` records that a provider
(the transaction sender) attests: for `queryHash`, run against committed store `version`, the
returned result set is committed by `resultRoot`. The named version MUST exist (1..current) or
the call reverts, so an attestation can never point at a store state that was never committed.
### Paid query receipt
On a priced store, `paidQuery` settles a payment on the AERE402 rail and mints a
`QueryReceipt` bound to `(storeId, queryHash, agentId, nonce)`. A retrieval attestation on a
priced store must cite a matching, unconsumed receipt; the receipt is consumed one-shot.
---
## 4. Post-quantum authentication
A post-quantum store's commit is authenticated by a Falcon-512 signature over a per-version
challenge:
```
challenge = keccak256(abi.encode(
COMMIT_DOMAIN, block.chainid, address(this),
storeId, version, vectorRoot, vectorCount, keccak256(bytes(uri))))
```
The challenge binds the chain, this contract, the store, the exact version, the committed root,
the vector count, and the URI. Because `version` is part of the challenge and advances by one
per commit, a signature is single-use and cannot be replayed onto a later version.
Verification uses the same input construction as `AerePQCAttestation.sol`, restricted to
Falcon-512 at `0x0AE1`:
```
falconSig = nonce(40) || esig (the envelope handed to commit)
input = pk(897) || sm
sm = sigLen(2, big-endian) || nonce(40) || challenge(32) || esig
esig = 0x29 || compressedSig, sigLen == esig.length
```
The precompile returns a 32-byte word, `0x..01` for valid and `0x..00` otherwise. An empty or
zero return, which is what a pre-fork node with no precompile would produce, is treated as
INVALID, so a stale caller can never record a commit. A commit whose Falcon signature does not
verify reverts `PQCVerificationFailed` and records nothing (fail-closed).
The live Falcon-512 precompile at `0x0AE1` was activated on mainnet 2800 at block 9,189,161
(see `besu_pqc_fork_proven_2026-07-11`). In the Hardhat test suite the precompile is mocked
(`MockPQCPrecompile`, installed at `0x0AE1` via `hardhat_setCode`), which parses the input at
the precompile's spec offsets; the real Falcon-512 encoding is proven separately against the
LIVE mainnet precompile by the `AerePQCAttestation` verification path [VERIFY].
---
## 5. Paid query on the AERE402 rail
Aere VSM references the existing `AERE402Facilitator` (the agentic payment rail); it does NOT
redeploy or fork it, and it introduces NO new token. Queries settle in whatever ERC-20 the store
owner selects (for example WAERE or USDC.e).
`paidQuery` works as an on-chain AERE402 provider endpoint:
1. The paying agent authorizes the payment with its EIP-712 `PaymentAuth` signature to the
facilitator, exactly as any AERE402 payment, with the vector store as the `payee`.
2. `paidQuery` calls `FACILITATOR.settle(...)`. Because the vector store is both the caller and
the payee, the facilitator's own `msg.sender == payee` guard is satisfied. The facilitator
debits the agent's prepaid balance, takes the 25-bps protocol fee to AereSink, and transfers
the remainder to the vector store.
3. The vector store measures the exact amount it received (a balance delta, robust to a changed
fee) and forwards it, in the same transaction, to the store's payment recipient. The contract
custodies no balance across calls, and it never holds an agent's funds.
4. A receipt is minted, bound to `(store, query)`, that a retrieval provider can later cite.
On a priced store, `attestRetrieval` reverts `PaymentRequired` when no valid receipt is cited,
`ReceiptConsumed` on reuse, and `ReceiptMismatch` when the receipt was paid for a different
store or query. This is the payment gate: a metered retrieval must be backed by a real,
on-chain settlement on the AERE402 rail.
---
## 6. Trust model
### What the on-chain part GUARANTEES [VERIFY]
- AUTHENTICITY of a committed root. A post-quantum store's commit carries a Falcon-512 signature
verified in full on-chain. An invalid signature records nothing.
- APPEND-ONLY HISTORY and IMMUTABILITY. No path rewrites or deletes a committed version; there is
no admin and no owner override over committed roots.
- OWNERSHIP. Every store is bound to an owner at creation; a classical store's commits require
the owner, a post-quantum store's commits require the Falcon key.
- RETRIEVAL AGAINST A COMMITTED VERSION. An attestation can only name a version that was actually
committed, and it records who attested it.
- INCLUSION. A consumer can prove, on-chain, that a returned vector was a member of a committed
version's `vectorRoot` (`verifyVectorInclusion`) and of a provider's attested `resultRoot`
(`verifyResultInclusion`).
- PAID, METERED RETRIEVAL. On a priced store, a retrieval attestation is backed one-for-one by an
AERE402 payment.
### What the on-chain part does NOT guarantee (OFF-CHAIN, [MEASURE])
- It does NOT compute embeddings, does NOT run the similarity search, and does NOT hold the
vectors. The embedding model, the nearest-neighbour index, and the raw vector data all live
off-chain.
- It does NOT and CANNOT prove that the embeddings are SEMANTICALLY GOOD, that the index is
well-built, or that the provider ran an honest similarity search. On-chain attestation proves
PROVENANCE and OWNERSHIP of a committed root and that a retrieval named a committed version. It
does NOT prove RELEVANCE. Semantic quality is an off-chain property, measured off-chain
[MEASURE].
- Inclusion in `resultRoot` proves the provider committed to returning that result; it does not
prove the result was the best or the correct nearest neighbour for the query.
### Scope boundary
Aere VSM is application-layer PQC authentication plus settlement. It does NOT change Aere
consensus: blocks are still produced and signed by Besu QBFT validators with classical ECDSA
(secp256k1). PQC-authenticated memory commits do NOT make consensus post-quantum. What is
post-quantum here is the AUTHENTICITY of a store commit, through the live Falcon-512 precompile.
---
## 7. Honest status
| Component | Status | Flag |
| --- | --- | --- |
| On-chain commit registry, versioned + append-only | Built, tested | [VERIFY] |
| Falcon-512 authenticated commits via `0x0AE1` | Built, tested (Falcon mocked in test) | [VERIFY] |
| Falcon-512 encoding vs the LIVE mainnet precompile | Proven separately (AerePQCAttestation) | [VERIFY] |
| Retrieval attestation, fail-closed on a missing version | Built, tested | [VERIFY] |
| Inclusion proofs (vectorRoot, resultRoot) | Built, tested | [VERIFY] |
| AERE402 paid-query gate | Built, tested against the real facilitator | [VERIFY] |
| Embedding model | Off-chain | [MEASURE] |
| Vector similarity search / index | Off-chain | [MEASURE] |
| Raw vector data + semantic quality / relevance | Off-chain | [MEASURE] |
The on-chain layer is built and tested. The semantic layer (embeddings, similarity search, and
the vector data) is off-chain research-frontier work, and its quality is measured off-chain, not
proven on-chain.
---
## 8. Test result
`npx hardhat test test/aere-vector-store.test.js`, run from `aerenew/contracts`, reports
8 passing:
1. commits a PQC store version 1, then version 2, preserving history append-only
2. rejects a tampered Falcon signature fail-closed (nothing recorded)
3. treats an empty precompile return (pre-fork / stale node) as invalid
4. records a retrieval attestation against a committed version and reverts on a missing version
5. verifies an inclusion proof for a committed vector and rejects a non-member
6. meters a priced store on the AERE402 rail and gates attestRetrieval on payment
7. classical store: only the owner may commit
8. createStore rejects a malformed Falcon key and inconsistent price config

View File

@ -0,0 +1,395 @@
# Aere Network Post-Quantum Finality: XMSS Aggregation Circuit Design
Research item #4, the off-chain half of the Aere Network Post-Quantum Finality Certificate.
Status header, read this first.
- IMPLEMENTED and TESTED: the per-validator VERIFICATION CORE. An RFC 8391
XMSS-SHA2_10_256 hash-based signature verifier (WOTS+ one-time signature plus the
XMSS Merkle authentication-path check that recovers the root public key), written
as a dependency-free, `no_std`, allocation-free, float-free, hash-only Rust crate
at `aerenew/pqc-fork/pq-finality-circuit/xmss-verify-core/`. It is validated
against the OFFICIAL github.com/XMSS/xmss-reference known-answer vector and passes
(see Section 7 for the real, run test result).
- NOT IMPLEMENTED, `[MEASURE]`, multi-week specialist work: the full zkVM
aggregation guest that runs this core over N validators, the SP1 proving pipeline,
and the end-to-end certificate. This document specifies that design. It does not
claim it works.
- UNCHANGED and fail-closed: the on-chain half
(`contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol` and
`AerePQAttestationKeyRegistry.sol`). Nothing in this work touches it. See Section 8.
Scope boundary, unchanged from the certificate design. This is the ADDITIVE
post-quantum finality-attestation READ path. Aere consensus is still classical ECDSA
QBFT. Validators keep signing ECDSA committed seals for block production 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 post-quantum finality is NOT live end to end. Aere consensus is not
post-quantum, and nothing here claims it is.
---
## 1. Where this sits
`AERE-PQ-FINALITY-CERTIFICATE.md` describes the whole feature. The on-chain verifier
consumes ONE succinct proof and binds its public values to the live attestation-key
registry. The proof is produced by an off-chain aggregation circuit that, inside a
zkVM, verifies that a quorum of validators hash-based-signed a finalized block.
That aggregation circuit has an inner obligation it discharges once PER VALIDATOR:
take a validator's committed hash-based public key and one signature, and confirm the
signature is a genuine hash-based signature under that key over the domain-bound block
message. For the XMSS-SHA2_10_256 scheme (RFC 8391, registry `SCHEME_XMSS`), that
inner obligation is exactly the code in `xmss-verify-core`: recover the XMSS root from
the signature and compare it to the validator's committed root.
This document specifies how the aggregation circuit wraps N instances of that inner
obligation into the single proof the on-chain verifier already accepts.
---
## 2. The inner obligation (IMPLEMENTED)
For one validator, XMSS-SHA2_10_256 verification (RFC 8391 sections 4.1.9 / 4.1.10,
single tree, height h = 10, n = 32, Winternitz w = 16, len = 67 chains, SHA-256):
1. `M' = H_msg( toByte(2,32) || R || root || toByte(idx,32) || M )`, the randomized,
root-bound message hash, where `M` is the domain-bound block message (Section 4.3).
2. `pk_ots = WOTS_PKFromSig(sig_ots, M', SEED, OTS-address(idx))`, completing each of
the 67 Winternitz chains from the signature value to its chain end.
3. `leaf = ltree(pk_ots, SEED, LTree-address(idx))`, the L-tree compression of the 67
WOTS+ public values to one n-byte leaf.
4. `node = compute_root(leaf, idx, auth, SEED, HashTree-address)`, folding the leaf
with the h = 10 authentication path.
5. The signature is VALID for `(root, SEED)` iff `node == root`.
All hashing uses the RFC 8391 keyed, address-domain-separated toolbox with
padding_len = n = 32: `F`, `H`, `H_msg`, and `PRF`, with per-address keys and bitmasks
derived by `PRF(SEED, ADRS)` over the 32-byte hash address. The reference crate is a
byte-for-byte model of the already-deployed on-chain `AereXmssVerifier.sol` and its
independent JS oracle, both validated against the same official vector.
Why XMSS verification is zkVM-tractable, honestly. It is hash-only: no floating point,
no FFT, no Gaussian sampling (the reason Falcon is hard in a circuit). Every step is a
SHA-256 call over fixed-size inputs, and SHA-256 is an SP1 precompile. The cost is a
LARGE but BOUNDED number of SHA-256 invocations (Section 6), not a novel arithmetic
gadget. That is what makes this the tractable first target.
Honest limits of the core. It verifies that a signature is valid for a given
`(root, SEED)`. It does NOT and cannot enforce the signer's one-time-per-leaf state:
XMSS is stateful, and reusing a leaf index breaks WOTS+ off-chain, not in the verifier.
The registry models rotation (Section 5); state discipline is the signer's duty.
---
## 3. The aggregation circuit (NOT implemented, `[MEASURE]`)
The guest program (SP1 zkVM in the reference design, matching the repo's existing
guests under `aerenew/zk-light-client/` and `aerenew/zk-circuits/`) takes a private
witness and commits public values.
### 3.1 Private witness
- `blockHash`, `blockHeight`: the finalized Aere block the certificate is about.
- `attestationDomain`: the 32-byte domain the on-chain verifier binds (Section 4.3).
- The ordered validator set: for each of the N enrolled validators (in the registry's
enrollment order, which fixes the root ordering): the validator address, its current
`keyEpoch`, and its current attestation public key bytes (for XMSS: `root || SEED`,
plus scheme id).
- For each validator that attested: its XMSS signature `(idx, R, wots[67], auth[10])`
over the domain-bound block message. Non-attesting validators carry a null marker.
### 3.2 What the circuit MUST enforce
1. RECOMPUTE the committee commitment. Rebuild `validatorSetRoot` from the witnessed
set exactly as `AerePQAttestationKeyRegistry.validatorSetRoot()` defines it
(Section 4.1), and ABORT unless it equals the committed root the circuit emits. This
is the membership check: a signature only counts if it is under a key the registry
committed. Because the flat commitment binds every validator's address, key-epoch,
and current key material, the circuit that reproduces it has proven each counted key
is a genuine member at the committed epoch.
2. VERIFY each counted attestation. For each attesting validator, run the Section 2
inner obligation against THAT validator's committed `(root, SEED)` over the message
of Section 4.3, and count it only if it recovers the committed root.
3. NEVER double-count. The circuit iterates the committed validator list in order and
admits at most one counted attestation per list position, so distinctness is
structural, not a runtime set-membership test.
4. COUNT and THRESHOLD. Let `quorumCount` be the number of DISTINCT valid attestations.
The circuit requires `quorumCount >= ceil(2N/3)` (the on-chain
`_quorumThreshold(n) = (2n + 2) / 3`), and ABORTS otherwise, so a proof cannot exist
below quorum.
5. EMIT the public values of Section 4.2.
The proof is succinct: its size and on-chain verification cost are fixed by the SP1
verifier, independent of N and of how many attestations were checked internally. That
independence lives entirely in the SP1 proof system, not in this circuit.
---
## 4. Binding to the on-chain verifier (the exact contract)
The circuit exists to satisfy the ALREADY-BUILT, UNCHANGED on-chain verifier. The
binding is exact and is the load-bearing honesty of the design.
### 4.1 The committee commitment (registry-defined)
`AerePQAttestationKeyRegistry.validatorSetRoot()` is a flat, ordered keccak commitment:
```
leaf_i = keccak256(abi.encode(validator_i, keyEpoch_i, keyHash_i))
root = keccak256(abi.encode(leaf_0, leaf_1, ..., leaf_{N-1}))
```
where `keyHash_i = keccak256(currentPubKey_i)`, or `bytes32(0)` if validator_i has no
key yet, and the ordering is enrollment order. The circuit MUST reproduce this
byte-for-byte, including the Solidity `abi.encode` framing.
`[VERIFY]` Two encoding details the guest implementation must match against the
deployed registry when built: (a) `abi.encode(validator_i, keyEpoch_i, keyHash_i)`
left-pads the 20-byte address and the `uint32` epoch to 32 bytes each, giving a 96-byte
pre-image; (b) `abi.encode(bytes32[] leaves)` prepends the ABI head for a single
dynamic array (a 32-byte offset word then a 32-byte length word) before the N leaves,
so `root = keccak256( 0x20-word || N-word || leaf_0 || ... || leaf_{N-1} )`. Both use
keccak-256, an SP1 precompile, distinct from the SHA-256 inside XMSS.
A Merkle tree is a drop-in alternative if per-leaf inclusion proofs are ever wanted
(the registry doc notes this). It is NOT needed here: because the circuit witnesses the
WHOLE set, recomputing the flat commitment is both sufficient and cheaper than N
logarithmic inclusion proofs. If N grows large enough that witnessing the whole set is
undesirable, switch the registry to a Merkle root and have the circuit check per-member
inclusion instead; the on-chain verifier is agnostic, it only compares roots.
### 4.2 The public values (fixed 192-byte layout)
The circuit MUST commit exactly the six-word layout
`AereFinalityCertificateVerifier._decode` expects:
```
abi.encode(
bytes32 blockHash, // the finalized Aere block hash
uint256 blockHeight, // its height
bytes32 validatorSetRoot, // recomputed IN the circuit per Section 4.1
uint256 validatorSetSize, // N committed under the root
uint256 quorumCount, // number of DISTINCT valid attestations, >= ceil(2N/3)
bytes32 attestationDomain // the domain tag of Section 4.3
)
```
The on-chain verifier then, in ONE call and independent of N, checks: the registry is
non-empty; `validatorSetRoot` equals the live registry root (a rotation invalidates old
certs); `validatorSetSize` equals the live N; `attestationDomain` equals its immutable
bound domain; `quorumCount >= ceil(2N/3)`; `blockHash != 0`; and the SP1 proof verifies
against the pinned circuit vkey. The circuit is trusted for the facts behind those six
words; the contract binds them to live state.
### 4.3 The attestation message (domain-bound)
Each validator signs `M`, the 32-byte value the on-chain verifier defines in
`attestationMessage`:
```
attestationDomain = keccak256(abi.encode(DOMAIN_PREFIX, chainId, registry))
M = keccak256(abi.encode(attestationDomain, blockHash, blockHeight))
```
`DOMAIN_PREFIX = keccak256("AerePQFinalityCertificate.v1")`. `M` is a keccak output;
the XMSS `H_msg` then hashes `M` (as its message bytes) with SHA-256 internally. Domain
binding blocks replay of a certificate to another chain, registry, or purpose. The
circuit computes `M` from the witnessed `attestationDomain`, `blockHash`, `blockHeight`
and checks every counted XMSS signature over it, then emits `attestationDomain` so the
contract can confirm it equals the domain it pinned at construction.
---
## 5. Choice of hash-based scheme, and the tradeoff
The registry already carries scheme labels: `SCHEME_XMSS` (RFC 8391 stateful few-time),
`SCHEME_WOTS` (Winternitz OTS+ one-time), `SCHEME_LEANSIG` (lean hash-based), and
`SCHEME_SLHDSA128S` (FIPS 205 stateless), plus `SCHEME_XMSSMT`. The circuit's inner
verifier is scheme-specific, so the scheme choice is a real design axis.
### 5.1 WOTS+ / XMSS (the implemented target)
- WOTS+ alone is ONE-TIME: a key signs exactly one message safely. XMSS layers a Merkle
tree of `2^h` WOTS+ keys under one long-term root, giving `2^h` safe signatures (here
`2^10 = 1024`) before the key is exhausted. Both are STATEFUL: the signer must never
reuse a leaf index. This is why attestation keys ROTATE, which
`AerePQAttestationKeyRegistry` already models with a monotonic per-validator
`keyEpoch` and an append-only key history. A validator that spends its `2^h` leaves
registers a successor key; the root changes; old certificates stop matching; new ones
bind to the new root. Rotation is expected, cheap, and fail-closed.
- Pro: standardized (RFC 8391, NIST SP 800-208), the most conservative post-quantum
assumption (security from SHA-256 collision and preimage resistance alone), and it
MATCHES the deployed on-chain `AereXmssVerifier.sol`, so the same bytes verify
on-chain and in-circuit.
- Con: SHA-256 is bit-oriented and comparatively EXPENSIVE to prove. The per-validator
cost is thousands of SHA-256 invocations (Section 6). With N small this is tractable;
it does not scale gracefully to thousands of validators.
`SCHEME_SLHDSA128S` (SLH-DSA, FIPS 205) is the STATELESS hash-based alternative: no
rotation discipline (a key signs many times safely), at the cost of larger signatures
and even more hashing per verify, so a heavier circuit. It is the right choice if
operators cannot be trusted to manage XMSS state; it trades circuit cost for removing
the stateful footgun.
### 5.2 leanSig / Poseidon2 (the field-native alternative)
leanSig is the hash-based signature line behind Ethereum's Lean Consensus / beam-chain
direction: a generalized-XMSS-style construction whose hash is Poseidon2 over a small
STARK-native field, chosen precisely for signature aggregation in a proof system.
- Pro: Poseidon2 is arithmetization-friendly. It is DRAMATICALLY cheaper inside a
FRI/STARK circuit than SHA-256 (which pays for bit operations the field does not model
natively). A leanSig aggregation circuit proves far faster per validator and scales to
much larger N, which is why Ethereum targets it for its very large validator set.
- Con: it is NEWER and less standardized than RFC 8391 XMSS, and it moves the hash off
SHA-256. To keep the on-chain binding exact, the registry key material and the
committee commitment would also need a consistent hashing story (either keep the
keccak commitment over Poseidon2-based public keys, or move the commitment to
Poseidon2 too); the on-chain `AereXmssVerifier.sol` SHA-256 path would not be the
in-circuit hash. It is the better ENDGAME for cost and scale; XMSS-SHA2 is the better
FIRST target for standards-alignment and for reusing the already-validated on-chain
verifier.
The tradeoff in one line: XMSS-SHA2 buys standardization, conservatism, and on-chain
reuse at a higher in-circuit hashing cost; leanSig/Poseidon2 buys a much cheaper,
more scalable circuit at the cost of novelty and a hashing story that diverges from the
deployed SHA-256 verifier. An ML-DSA-in-zkVM approach (FIPS 204, integer arithmetic) is
a further alternative with an easier circuit than SHA-256-XMSS, but it is LATTICE-based,
not hash-based, so it does NOT carry the hash-only quantum-safety argument and is out of
scope for the hash-based certificate.
Aere Network's live-first advantage is honest and specific: with a small validator set
(N = 7 today, N >= 9 planned) the XMSS-SHA2 aggregation verifies at most N hash-based
signatures per certificate, a tractable proving workload NOW, whereas Ethereum's
hundreds of thousands of validators force the leanSig research runway. The small set
that makes Aere's honest-quorum premise an operator assumption is the same property that
makes post-quantum finality aggregation buildable today.
---
## 6. Structural cost (derivable) and performance (`[MEASURE]`)
The following op counts are STRUCTURAL: they follow from the XMSS-SHA2_10_256 parameters
and the toolbox definitions, and are derivable without running anything. They are NOT a
performance measurement.
Per SHA-256-toolbox primitive: `PRF` and `F` hash 96-byte inputs; `H` hashes a 128-byte
input; each `F` step needs 2 `PRF` (key, mask) plus one `F` hash (3 SHA-256 calls); each
`H` node needs 3 `PRF` (key, two masks) plus one `H` hash (4 SHA-256 calls).
Per-validator XMSS-SHA2_10_256 verify (worst case, chains completed from the low end):
| Stage | Units | SHA-256 calls (worst case) |
| ----------------- | ------------------------------ | -------------------------- |
| `H_msg` | 1 hash over ~129 bytes | 1 |
| WOTS_PKFromSig | up to 67 chains x 15 `F` steps | up to ~3015 |
| L-tree | 66 `H` nodes | ~264 |
| Auth path (h=10) | 10 `H` nodes | ~40 |
| Per validator | | up to ~3320 SHA-256 calls |
For N = 7 that is on the order of ~23,000 SHA-256 invocations across the aggregation,
plus a handful of keccak-256 calls for the committee commitment and the message binding.
Typical (not worst) WOTS cost is roughly half, since the average completed chain length
is about w/2. These counts are exactly why SHA-256 is the cost driver and why
leanSig/Poseidon2 is the cheaper long-run hash.
`[MEASURE]` and NOT run here (each depends on the actual SP1 guest build, the sha256/
keccak precompile configuration, the prover hardware, and the proof mode):
- RISC-V cycle count per validator and per certificate inside the SP1 zkVM.
- Proving latency and memory for N = 7 and N = 9, on a given prover or the Succinct
prover network.
- On-chain `verifyProof` gas against the REAL Succinct gateway (the contract is
currently tested against a mock gateway).
- Native STARK vs Groth16-wrap tradeoff. A Groth16 wrap over BN254 shrinks the on-chain
proof but REINTRODUCES a Shor-breakable pairing leg, forfeiting the end-to-end
quantum-safety of Section 4 of the certificate doc. A native hash-based STARK proof
(verified by the `AerePQStarkVerifier` line) keeps the proof itself post-quantum. This
choice is a `[MEASURE]` cost-vs-quantum-safety decision, not settled here.
Scope caveat (2026-07-19 finding): the `AerePQStarkVerifier` skeleton (0x0AE8) built so
far is a BabyBear + FRI verifier that targets Aere's OWN Plonky3 STARKs. It does NOT
verify an SP1 6.1.0 proof, because the pinned SP1 6.1.0 is a Hypercube / BaseFold
(KoalaBear multilinear) system, not BabyBear FRI. So the native-STARK option here means
proving the aggregation as an Aere-native BabyBear/FRI STARK (or retargeting 0x0AE8 to
the SP1 Hypercube stack, a separate ~22 to 32 person-week effort), not verifying an SP1
zkVM proof directly. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`.
---
## 7. What was actually built and the REAL test result
Built at `aerenew/pqc-fork/pq-finality-circuit/xmss-verify-core/`:
- `src/sha256.rs`: a dependency-free `no_std` SHA-256 (self-checked against FIPS 180-4
`SHA256("abc")` and `SHA256("")`). A production SP1 guest swaps this for the
SP1-patched `sha2` precompile crate; the bytes are identical.
- `src/xmss.rs`: the RFC 8391 XMSS-SHA2_10_256 verification core (`chain_lengths`,
`wots_pk_from_sig`, `l_tree`, `compute_root`, `xmss_recover_root`, `xmss_verify`),
`no_std`, allocation-free, float-free, hash-only.
- `src/vectors.rs`: the official vector, GENERATED by `scripts/gen_vectors.mjs` from the
committed `contracts/test/fixtures/xmss-sha2_10_256-kat.json`
(github.com/XMSS/xmss-reference, oid=1, idx=512, msg=0x25), so the Rust vector is
provably derived from the official fixture with no hand transcription.
- `tests/kat.rs`: the known-answer validation.
Real result of `cargo test --offline` (run, not asserted from memory): 12 tests
passed, 0 failed. The genuine official signature recovers the official root
`0x9d898033e37af48e6a116f8b15651cc26773467007ad19375d38c23c690c3483` and verifies; and
every tamper (a WOTS+ value, the checksum chain, an authentication-path node, the
message, the leaf index, the claimed root, the R randomizer) fails to verify. The two
SHA-256 self-checks match FIPS 180-4.
This validates the INNER obligation of Section 2 only. It is not a proof, not a zkVM
run, and not an aggregation.
---
## 8. On-chain side is unchanged and fail-closed
This work adds an off-chain reference crate and this document. It does NOT modify
`AereFinalityCertificateVerifier.sol` or `AerePQAttestationKeyRegistry.sol`, and does
not touch their tests. The on-chain verifier remains fail-closed exactly as before: it
rejects unless the registry is non-empty, the proven root and size match the live
registry, the domain matches, `quorumCount >= ceil(2N/3)`, the block hash is non-zero,
AND the SP1 proof verifies against the pinned vkey through the immutable gateway. Until
a real aggregation guest and a real proof exist, no certificate can pass the real
gateway, which is the correct fail-closed behavior.
---
## 9. Effort estimate for the remaining `[MEASURE]` work
Honest, and a range, not a promise. The verification core (done) is the small part.
The remaining full zkVM guest plus proving pipeline is multi-week specialist work:
- Guest program: port `xmss-verify-core` into an SP1 guest, add the keccak committee
commitment and message binding, the N-validator loop, distinct-count, and the
threshold, and wire the six-word public-values output. Roughly 1 to 2 weeks including
matching the `abi.encode` framing of Section 4.1 against the deployed registry.
- Host and witness assembly: collect attestations, build the private witness, run the
prover. Roughly 1 week.
- Proving, tuning, and MEASURE: cycle counts, latency, memory, real-gateway gas, and the
native-STARK vs Groth16 decision. Roughly 1 to 3 weeks, hardware-dependent.
- Audit of the guest before any mainnet vkey is pinned. Separate, founder-gated.
Total on the order of several weeks of focused specialist work before an end-to-end
certificate could be produced, plus audit. Do NOT describe the certificate as working
end-to-end until that work is done and measured.
---
## 10. References
- On-chain half (unchanged): `contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol`,
`contracts/contracts/pqfinality/AerePQAttestationKeyRegistry.sol`, and
`contracts/test/AerePQFinalityCertificate.test.js`.
- Feature doc: `aerenew/docs/AERE-PQ-FINALITY-CERTIFICATE.md`.
- Deployed on-chain XMSS verifier and its official vector:
`contracts/contracts/pqc/AereXmssVerifier.sol`, `contracts/test/xmssVerifier.test.js`,
`contracts/test/fixtures/xmss-sha2_10_256-kat.json`.
- SP1 guest / core-crate pattern this crate mirrors: `aerenew/zk-light-client/guest/`,
`aerenew/zk-light-client/qbft-finality/`, `aerenew/zk-circuits/recursive-aggregation-scale/`.
- Standards: RFC 8391 (XMSS), NIST SP 800-208 (stateful hash-based signatures), FIPS 205
(SLH-DSA), FIPS 180-4 (SHA-256).
```

View File

@ -0,0 +1,98 @@
# Post-Quantum Finality Certificate (#4): XMSS aggregation circuit, verification core
Date: 2026-07-19
## What was asked
Advance the research core of Aere Network's Post-Quantum Finality Certificate: the
off-chain XMSS/leanSig aggregation circuit that a zkVM would run to prove a quorum of
validators hash-based-signed a block. Implement the tractable, independently-testable
VERIFICATION CORE now (validated against real vectors) and specify the rest as design.
Never claim the aggregation proof works end to end.
## What was found (grounding)
- OFFICIAL RFC 8391 XMSS-SHA2_10_256 known-answer vector already committed at
`aerenew/contracts/test/fixtures/xmss-sha2_10_256-kat.json`
(github.com/XMSS/xmss-reference, oid=1, idx=512, msg=0x25, pubRoot 0x9d898033...c3483).
- Deployed on-chain XMSS verifier + independent JS oracle:
`contracts/contracts/pqc/AereXmssVerifier.sol`, `contracts/test/xmssVerifier.test.js`.
- SP1 guest style is a clean "pure-Rust core crate + thin no_main guest + std test"
split (`aerenew/zk-light-client/qbft-finality` + `guest`), sha2/k256 SP1-patched.
- On-chain public-values layout (192 bytes, 6 words) and quorum rule read from
`contracts/contracts/pqfinality/AereFinalityCertificateVerifier.sol`:
abi.encode(bytes32 blockHash, uint256 blockHeight, bytes32 validatorSetRoot,
uint256 validatorSetSize, uint256 quorumCount, bytes32 attestationDomain);
threshold `_quorumThreshold(n) = (2n+2)/3 = ceil(2N/3)`.
- Committee commitment (registry-defined, flat keccak):
leaf_i = keccak256(abi.encode(validator_i, keyEpoch_i, keyHash_i)),
keyHash_i = keccak256(pubKey_i), root = keccak256(abi.encode(leaves)).
- Attestation message: attestationDomain = keccak256(abi.encode(DOMAIN_PREFIX, chainId,
registry)); M = keccak256(abi.encode(attestationDomain, blockHash, blockHeight)).
## What was built (IMPLEMENTED + TESTED)
Dir: `aerenew/pqc-fork/pq-finality-circuit/`
- `xmss-verify-core/` Rust crate, ZERO external deps, `no_std`, allocation-free,
float-free, hash-only, deterministic (models the aggregation guest's per-validator
inner logic):
- `src/sha256.rs` dependency-free SHA-256 (FIPS 180-4). Note: production SP1 guest
swaps in the SP1-patched `sha2` precompile; identical bytes.
- `src/xmss.rs` RFC 8391 XMSS-SHA2_10_256 verify core: chain_lengths, WOTS_PKFromSig,
L-tree, compute_root, xmss_recover_root, xmss_verify. Byte-for-byte model of the
on-chain AereXmssVerifier.sol / JS oracle.
- `src/vectors.rs` official vector, GENERATED from the committed KAT (provenance,
no hand transcription).
- `tests/kat.rs` known-answer validation.
- `scripts/gen_vectors.mjs` regenerates vectors.rs from the committed JSON.
- `README.md`.
## REAL vector-test result (run, not asserted from memory)
`cargo test --offline` in `xmss-verify-core/`: 12 passed, 0 failed.
- recovers_the_official_reference_root: OK (recovers pubRoot 0x9d898033...c3483)
- accepts_the_official_reference_signature: OK
- rejects tampered: wots value, wots tail/checksum, auth node, message, leaf index,
wrong claimed root, tampered R randomizer: all fail to verify (OK)
- sha256 self-checks vs FIPS 180-4 ("abc", ""): OK
- chain_lengths checksum shape: OK
## Design doc written
`aerenew/docs/AERE-XMSS-AGGREGATION-CIRCUIT.md`:
- Full aggregation-circuit design: per validator recover XMSS root, recompute the
registry committee commitment (membership), verify each attestation, structural
distinctness, count >= ceil(2N/3), emit the exact 192-byte public values bound to the
on-chain verifier; message + domain binding spelled out.
- Scheme choice + tradeoff: WOTS+/XMSS (standardized RFC 8391, most conservative,
matches on-chain verifier, one-time/few-time so rotation required, which
AerePQAttestationKeyRegistry already models; SHA-256 expensive in-circuit) vs
leanSig/Poseidon2 (SP1/STARK-native field, dramatically cheaper + scalable, but newer
and hashing story diverges from the deployed SHA-256 verifier); SLH-DSA stateless
alternative; ML-DSA is lattice not hash-based (out of scope for the hash-only claim).
- Structural cost table (derivable): ~3320 SHA-256 calls per validator worst case,
~23k for N=7.
- Effort estimate: several weeks specialist work + audit for the full guest + proving.
- Honest status labels throughout.
## HONESTY flags
- [MEASURE] / NOT implemented: the zkVM aggregation guest, SP1 proving pipeline,
end-to-end certificate, cycle counts, proving latency/memory, real-gateway verify gas,
native-STARK vs Groth16-wrap decision.
- [VERIFY]: exact Solidity `abi.encode` framing the guest must reproduce for the
committee commitment (address/uint32 left-padding to 96-byte leaf pre-image; the
bytes32[] ABI head of a 0x20 offset word + N length word before the leaves). Flagged
in the doc; to be matched against the deployed registry when the guest is built.
- Verification core is REAL + tested against the official vector (above).
## On-chain side
UNCHANGED. AereFinalityCertificateVerifier.sol and AerePQAttestationKeyRegistry.sol were
only Read, never modified; their tests untouched. Still fail-closed: no certificate can
pass the real gateway until a real guest + real proof exist.
## Scope boundary held
Additive PQ finality attestation only. Base QBFT consensus is still classical ECDSA;
this does not flip it. PQ finality is NOT live end to end.

386
AIP-PQ-TX-2026-07-18.md Normal file
View File

@ -0,0 +1,386 @@
# AIP-8: Post-Quantum-Authorized Transaction Envelope (EIP-2718 Type 0x2A)
## Preamble
| Field | Value |
| --- | --- |
| AIP | 8 |
| Title | Post-Quantum-Authorized Transaction Envelope (EIP-2718 Type 0x2A) |
| Author | AERE Foundation |
| Type | Standards Track |
| Category | Core |
| Status | Draft |
| Created | 2026-07-18 |
| Requires | 7 |
| Ratification | Foundation-ratified (pre-decentralization) |
## Abstract
This AIP specifies a new EIP-2718 typed transaction, `TransactionType = 0x2A`, whose
**sender authorization is a NIST post-quantum signature** (Falcon-512 or ML-DSA-44)
rather than a secp256k1 ECDSA signature. The transaction carries a scheme selector, a
raw PQC public key, and a PQC signature; the sender address is derived from the public
key, and the transaction is valid only if the signature verifies against the exact same
native precompiles that are already live on AERE mainnet chain 2800 (Falcon-512 `0x0AE1`,
ML-DSA-44 `0x0AE3`, activated at block 9,189,161 per AIP-7). It makes ordinary value and
contract-call transactions authorizable by a quantum-resistant key with no smart-account
indirection. This is a **base-layer execution change requiring a coordinated Besu hard
fork**; it is NOT yet live. It is explicit that this does not change how AERE blocks are
proposed, signed, or committed: consensus remains classical secp256k1 ECDSA QBFT and is
not post-quantum. A deploy-today reference path that needs no fork
(`AerePQCTxAccount.sol`, an ERC-4337 account) accompanies this AIP.
## Motivation
AERE has live, mainnet-proven post-quantum signature verification at the precompile layer
(AIP-7) and usable post-quantum authorization at the account layer (AerePQCAccount,
AereThresholdAccount, AerePQCSocialRecoveryModule). What is still missing is the most
basic object of all: an ordinary externally-originated transaction whose right to spend an
account is a post-quantum signature.
Today every transaction on chain 2800, including one that calls a post-quantum contract,
is itself authorized by a secp256k1 ECDSA signature. The account's ability to move its
native balance rests on ECDSA. A cryptographically-relevant quantum computer running
Shor's algorithm breaks secp256k1: it recovers the private key from the public key, and on
any account that has ever sent a transaction the public key is already on-chain. Account-
layer PQC (a 4337 smart account) closes this for smart-contract wallets, but it cannot
protect a plain externally-owned account (EOA), and it adds a bundler/EntryPoint and a
deployment step. A base-layer post-quantum transaction type closes the gap for the EOA
itself: the account is a hash of a PQC public key, and only a PQC signature can spend it.
The goal is a transaction type that (1) reuses the audited, mainnet-live precompile verify
paths verbatim (no new cryptography), (2) is a pure function of the transaction bytes for
validation (like ECDSA sender recovery, so mempool validation reads no state), (3) fits far
under the Fusaka EIP-7825 per-transaction gas cap, and (4) is a strictly additive,
client-only hard fork with no re-genesis, exactly like AIP-7.
## Specification
The keywords MUST, SHOULD, and MAY are to be interpreted as in RFC 2119.
### Transaction type byte
The transaction type is `0x2A`.
EIP-2718 constrains a valid `TransactionType` to the range `[0x00, 0x7f]`: a first byte
`>= 0xc0` is ambiguous with the RLP list header of a legacy transaction and is excluded,
and EIP-2718 recommends implementers stay within `0x00`..`0x7f`. Within that range the
Ethereum L1 assignments are `0x01` (EIP-2930), `0x02` (EIP-1559), `0x03` (EIP-4844), and
`0x04` (EIP-7702); AERE recognizes all four. `0x2A` is chosen because it is unassigned by
any Ethereum L1 EIP and sits well below the `0x64`..`0x7f` band that existing L2s have
informally colonized (Arbitrum `0x64`..`0x6c`, zkSync Era `0x71`+, Optimism deposit
`0x7e`, Celo `0x7b`..`0x7d`), which minimizes the chance that an imported third-party tool
misparses an AERE PQ transaction as some other chain's type. `0x2A` is reserved henceforth
in the AERE chain specification for this purpose. If Ethereum L1 later assigns `0x2A`, AERE
will re-evaluate to preserve equivalence, as AIP-7 commits to for the precompile band.
### Signature-scheme selector
A one-byte `pq_scheme` selects the NIST scheme and the precompile that validates it. The
values match AIP-7 and AerePQCAttestation:
| `pq_scheme` | Scheme | Precompile | Public-key length | Signature envelope |
| --- | --- | --- | --- | --- |
| `0x01` | Falcon-512 | `0x0AE1` | 897 bytes (header `0x09`) | `nonce(40) || esig` (esig starts `0x29`) |
| `0x03` | ML-DSA-44 | `0x0AE3` | 1312 bytes | `sig`, exactly 2420 bytes |
`pq_scheme` `0x02` (Falcon-1024, `0x0AE2`, 1793-byte pk) and `0x04` (SLH-DSA-128s, `0x0AE4`,
32-byte pk, 7856-byte sig) MAY be enabled by the same rules; they are omitted from the
mandatory set because Falcon-512 (NIST level 1, smallest signature) and ML-DSA-44 (FIPS 204,
fixed-size, conservative) span the practical trade-off between signature size and margin.
Any other value MUST be rejected as an invalid transaction.
### RLP wire format
A type-`0x2A` transaction is the concatenation of the type byte and the RLP encoding of a
12-element list:
```
0x2A || rlp([
chain_id, # scalar; MUST equal 2800 for AERE mainnet
nonce, # scalar; the PQC-derived sender's account nonce
max_priority_fee_per_gas, # scalar (EIP-1559 semantics)
max_fee_per_gas, # scalar (EIP-1559 semantics)
gas_limit, # scalar
to, # 20-byte address, or empty (0x) for contract creation
value, # scalar
data, # byte string
access_list, # EIP-2930 [[address, [storageKey, ...]], ...]
pq_scheme, # single byte, 0x01 or 0x03 (see selector table)
pq_public_key, # raw NIST public key for pq_scheme
pq_signature # signature envelope for pq_scheme (see selector table)
])
```
The fee, gas, `to`, `value`, `data`, and `access_list` fields are identical in meaning to
EIP-1559 (type `0x02`) with an EIP-2930 access list, so existing execution and fee-market
logic is unchanged. The three trailing fields replace the ECDSA `(y_parity, r, s)` triple.
The transaction hash is `keccak256(0x2A || rlp([...all 12 fields...]))`, as for any typed
transaction.
### Sender derivation
The sender is derived from the PQC public key and the scheme, not from signature recovery:
```
sender = keccak256(pq_scheme || pq_public_key)[12:32] # last 20 bytes
```
Binding the address to `pq_scheme` as well as the key means the same raw key bytes under a
different scheme selector yield a different account, so there is no cross-scheme aliasing. A
user migrates to post-quantum custody by moving assets to their `sender` address; from then
on only a valid PQC signature can spend it. Unlike ECDSA, the public key is transmitted in
full on every transaction (the verify precompiles are stateless), which is safe: Falcon and
ML-DSA remain secure with the public key exposed. This is the point of the design, not a
weakness.
### Signing message and verification
`pq_signature` MUST be a valid signature, under `pq_public_key` and `pq_scheme`, over the
32-byte digest:
```
sig_hash = keccak256(0x2A || rlp([
chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
to, value, data, access_list, pq_scheme, pq_public_key
])) # the 12-field list WITHOUT pq_signature
```
Validation MUST assemble the precompile input for `pq_scheme` exactly as
AerePQCAttestation / AerePQCKeyRegistry do, with `sig_hash` as the signed message, and
accept the transaction only if the corresponding precompile returns the 32-byte
"valid" word `0x…01`:
- Falcon-512: `input = pk(897) || sm`, where `sm = sigLen(2, big-endian) || nonce(40) ||
sig_hash(32) || esig` and `sigLen == esig.length`; call `0x0AE1`.
- ML-DSA-44: `input = pk(1312) || sig(2420) || sig_hash(32)`; call `0x0AE3`.
The client MUST use the same audited Bouncy Castle 1.83 verify path the precompile wraps, so
that transaction validation and an in-EVM `STATICCALL` to the precompile agree bit-for-bit
on every `(pq_public_key, sig_hash, pq_signature)` triple. A transaction whose signature
does not verify has no valid sender and is invalid: it MUST be rejected from the mempool and
MUST NOT be included in a block, exactly as an ECDSA transaction with an unrecoverable
signature is.
### Nonce and replay handling
- **Cross-chain replay** is prevented by `chain_id` inside both the transaction and
`sig_hash`: a signature for chain 2800 does not verify for any other chain id.
- **Intra-chain replay and ordering** are enforced by `nonce`, the account nonce of the
PQC-derived `sender`, tracked in world state and incremented on inclusion, identical to an
ECDSA account. A given `(sender, nonce)` can be mined at most once.
- **Cross-protocol replay** (reusing a PQ tx signature as an AerePQCAttestation,
AerePQCKeyRegistry, or ERC-4337 authorization, or vice-versa) is prevented because those
surfaces sign structurally different messages: attestation/registry/account challenges are
`keccak256(abi.encode(DOMAIN, ...))` or `abi.encodePacked(domain, hash)` byte strings that
can never equal the `0x2A || rlp(...)` preimage of `sig_hash`. The leading type byte `0x2A`
in the signed preimage is the domain tag for this transaction type, mirroring how EIP-1559
signs `keccak256(0x02 || rlp(...))`.
### Gas accounting
Intrinsic gas for a type-`0x2A` transaction is:
```
intrinsic = 21000
+ calldata_gas(entire type-0x2A payload, including pq_public_key + pq_signature)
+ access_list_gas # EIP-2930: 2400 per address, 1900 per storage key
+ PQ_VERIFY_GAS[pq_scheme] # fork constant, MUST equal the live precompile verify gas
```
`PQ_VERIFY_GAS` MUST equal the marginal verify-op gas the corresponding precompile charges,
which AIP-7 measured on the pre-activation scratch fork (chain 28099) and carried to
mainnet:
| Scheme | `PQ_VERIFY_GAS` (measured, AIP-7) |
| --- | --- |
| Falcon-512 (`0x0AE1`) | 40,000 |
| ML-DSA-44 (`0x0AE3`) | 55,000 |
| Falcon-1024 (`0x0AE2`) | 75,000 |
| SLH-DSA-128s (`0x0AE4`) | 350,000 |
The dominant *additional* cost versus an EIP-1559 transaction is the calldata for the public
key and signature. Approximate PQ-material sizes and the resulting calldata component (to be
measured on the scratch fork; calldata is priced at EIP-2028 rates, subject to the EIP-7623
floor):
| Scheme | pk bytes | sig-envelope bytes | PQ material | approx. calldata gas |
| --- | --- | --- | --- | --- |
| Falcon-512 | 897 | ~707 (40 + 1 + ~666) | ~1,605 B | ~24,000 (to be measured) |
| ML-DSA-44 | 1,312 | 2,420 | ~3,732 B | ~56,000 (to be measured) |
A complete Falcon-512 PQ transaction therefore lands near `21,000 + ~24,000 + 40,000 ≈
85,000` gas of intrinsic cost before execution, consistent with AIP-7's measured
end-to-end Falcon-512 verify-and-record transaction of 86,336 gas; a ML-DSA-44 PQ
transaction lands near `21,000 + ~56,000 + 55,000 ≈ 132,000`. Both are a tiny fraction of
the EIP-7825 per-transaction cap of 16,777,216 (2^24), leaving the full block-execution
budget for the call itself.
`PQ_VERIFY_GAS` is folded into intrinsic gas so the sender pays for the verification work
that validators perform. Because signature validity is a precondition of transaction
validity, a transaction with an invalid signature is never included and thus is never
charged on-chain, exactly as with a bad ECDSA signature; it simply cannot be mined.
### Base-layer activation sketch (Besu fork)
The change is a strictly additive execution-layer fork of the AERE Besu build (base
`hyperledger/besu:26.4.0`, the same tree that carries the AIP-7 precompiles). No re-genesis,
no state migration. The implementation touches, at minimum:
1. **TransactionType.** Add `PQ_AUTHORIZED((byte) 0x2A)` to
`org.hyperledger.besu.datatypes.TransactionType`.
2. **Codec.** Add a `PQAuthorizedTransactionDecoder` / `PQAuthorizedTransactionEncoder`
implementing the 12-field RLP layout above and register them in the `TransactionDecoder` /
`TransactionEncoder` dispatch. Extend the `Transaction` model to carry `pqScheme`,
`pqPublicKey`, and `pqSignature`, and override sender computation for this type to
`keccak256(pqScheme || pqPublicKey)[12:]`, defined only when the PQC signature verifies.
3. **PQ signature verifier.** A `PQAuthorizedSignatureVerifier` that calls the exact same
Bouncy Castle 1.83 BCPQC verify path (`AereFalconSupport.verify`, ML-DSA `verifyInternal`)
the `0x0AE1` / `0x0AE3` precompiles wrap. Zero new cryptography; reuse the precompile
adapters through the same classloader-isolation boundary AIP-7 established.
4. **Validation.** In `MainnetTransactionValidator` (via `TransactionValidatorFactory`), for
type `0x2A`: reconstruct `sig_hash`, verify the PQC signature (reject with an INVALID
reason on failure), derive the sender, and validate nonce, balance, and intrinsic gas as
usual.
5. **Gas.** Extend the `GasCalculator` with `pqAuthorizationIntrinsicGasCost(scheme, payload)`
returning the calldata component plus `PQ_VERIFY_GAS[scheme]`, wired into
`transactionIntrinsicGasCost`.
6. **Mempool and wire.** Teach `TransactionPool` and the eth/68 `Transactions` /
`PooledTransactions` codecs to carry the larger typed body; expose the type in the JSON-RPC
transaction serialization (`eth_getTransactionByHash`, receipts).
7. **Fork gating.** Add an `AerePQTx` milestone to `ProtocolScheduleBuilder` / the chain spec,
activated at a future block or timestamp distinct from the AIP-7 precompile activation.
Before activation, a `0x2A` first byte is an unknown type and is rejected; after
activation it is valid. Because chain 2800 runs seven QBFT validators under one operator on
one client, activation is a flag-day client upgrade across the validator set, exactly as in
AIP-7. A non-upgraded validator would reject the type and fork off, so the upgrade is
mandatory at the fork block.
8. **Differential testing.** Gate ship on a test that, for the NIST KAT vectors in
`pqc-fork/kat` and randomized cases, asserts the transaction-validation verify agrees
bit-for-bit with a `STATICCALL` to the matching precompile over the same
`(pk, sig_hash, sig)`, plus round-trip encode/decode and a full mine-and-receipt path on an
isolated testnet.
Consensus is untouched: QBFT proposal, the committed-seal ECDSA signatures, and block
structure are unchanged; type `0x2A` is simply another transaction a block may contain, with
its own receipt type.
## Rationale
**Full public key in every transaction, not a registry keyId.** An alternative shrinks
calldata by replacing `pq_public_key` with a `keyId` into AerePQCKeyRegistry
(`0x1eCa…3691`). It is rejected for the base type because it makes transaction validation
depend on world state (a registry lookup), which breaks the property that a transaction's
validity is a pure function of its own bytes, the same property ECDSA sender recovery has,
and which mempool validation relies on. The stateless full-key form keeps validation pure
and matches how the live precompiles already work. A registry-indexed companion type could be
specified later once the state-dependency implications for the mempool are worked through.
**Sender = hash of (scheme, key), reusing the 20-byte address space.** This keeps the PQ
account indistinguishable from any other address to every downstream contract, tool, and
explorer: balances, `CALL`, `msg.sender`, and logs all behave normally. The only thing that
changes is who may originate a transaction from that address.
**Type `0x2A` over a higher value.** Staying in the low half of the valid range and away
from the L2-colonized `0x64`..`0x7f` band reduces cross-ecosystem tool confusion, at no cost.
**A base-layer type at all, given 4337 exists.** ERC-4337 already gives post-quantum
authorization for smart-contract wallets and is the recommended path today (see the reference
implementation). But a 4337 account cannot protect a plain EOA, requires a deployed contract
and a bundler, and pays EVM-execution overhead for validation. The base type is the only way
to make an *ordinary* account post-quantum, and it prices verification at the native
precompile cost rather than in-EVM. The trade-off is that it requires a hard fork and touches
the entire transaction-decoding surface of the ecosystem, which is exactly why it is filed as
a Draft with a shipping 4337 alternative rather than activated immediately.
## Backwards Compatibility
Additive. Existing legacy, `0x01`, `0x02`, `0x03`, and `0x04` transactions are unaffected.
Before the `AerePQTx` fork block, a `0x2A` transaction is an unknown type and is rejected by
all nodes; after it, all nodes accept it. There is no change to existing accounts, contracts,
receipts of other types, or the genesis state root.
Ecosystem tooling that decodes raw transactions (wallets, explorers, indexers, the RPC
transaction serializer, hardware-wallet firmware, signing libraries) MUST learn type `0x2A`
to build, sign, display, or index these transactions. Tools that only handle types they
recognize will treat a `0x2A` transaction as opaque or unsupported rather than misdecode it,
because the type byte is self-describing. This ecosystem surface is the principal cost of the
change and the reason the ERC-4337 path is preferred for immediate use.
Because AERE runs all validators under one operator and one client, activation is a
mandatory flag-day client upgrade across the validator set at the fork block, as in AIP-7. A
validator that had not upgraded would reject the type and fork.
## Security Considerations
**This change does not make AERE consensus post-quantum.** Validators continue to sign
classical secp256k1 QBFT committed seals; blocks are proposed and committed exactly as today.
Type `0x2A` makes an individual transaction's *sender authorization* post-quantum. It does
not make block production, finality, or the validator set post-quantum, and any claim to that
effect on the basis of this AIP would be false and must not be made.
**A valid signature is not authorization beyond spend rights.** A "valid" precompile decision
establishes only that `pq_signature` is a valid signature by `pq_public_key` over `sig_hash`.
Freshness and ordering come from `nonce`; scope comes from the tx fields themselves. There is
no key rotation or recovery at this layer: losing the PQC private key loses the account, the
same as losing an ECDSA key. Users who need rotation or social recovery should hold assets in
an ERC-7579 account guarded by AerePQCSocialRecoveryModule instead of, or in addition to, a
PQ EOA.
**Single operator, one client (no client-diversity safety net).** Chain 2800's seven QBFT
validators are one operator on one execution client. A consensus bug in the PQ-transaction
validation path (for example a decoder edge case, or a divergence between the tx-validation
verify and the precompile verify) would not be caught by a second live client, because there
is none. This raises the bar for external audit and for the mandatory bit-for-bit
differential testing in the activation sketch.
**Determinism.** Transaction validation, like a precompile, must be deterministic and read no
node-local state or block context beyond the transaction and the account nonce/balance.
Falcon verification MUST use the integer-only verify path (never the floating-point signing
path). Malformed structure MUST be rejected deterministically as an invalid transaction, not
faulted.
**Public-key exposure is intended.** The public key is on-chain from the account's first
transaction. This is safe for Falcon and ML-DSA and is the mechanism by which the account is
quantum-resistant; it is called out only to preempt the ECDSA-era intuition that an unspent
address "hides" its key.
**Not live; external audit pending.** This is a Draft specification and a reference-
implementation sketch. Nothing here is deployed on chain 2800. The precompiles it reuses
carry an internal self-audit only (AIP-7). This transaction type MUST NOT secure material
value until it is implemented, differentially tested against the live precompiles, and
covered by an external security audit.
## Reference Implementation and On-Chain Deployment
**Not yet live.** The base-layer fork above is a specification and a Besu implementation
sketch, not a deployed change.
**Deploy-today reference path (no fork required).** The same user-visible guarantee, "a
transaction is authorized by a post-quantum signature and nothing else", is delivered today,
with no base-layer change, by an ERC-4337 v0.7 smart account whose sole owner is a NIST PQC
key and whose `validateUserOp` verifies via the live precompiles:
- Contract: `contracts/contracts/pqc/AerePQCTxAccount.sol` (this repository).
- Tests: `contracts/test/AerePQCTxAccount.test.js` (MockPQCPrecompile across all four schemes
plus a genuine Falcon-512 path through the NIST-KAT-proven AereFalcon512Verifier).
- **Verified 2026-07-18:** compiled and tested under the canonical Hardhat toolchain,
`npx hardhat compile` then `npx hardhat test test/AerePQCTxAccount.test.js`. The contract
compiles clean and all 20 tests pass: the mock verifier across all four schemes, the
domain-separation and single-use replay checks, and two genuine Falcon-512 paths through the
NIST-KAT-proven verifier. This bullet records an actual machine run; it should be read as a
verified result, not an untested present-tense assertion.
- It calls the live precompiles (`0x0AE1`..`0x0AE4`) directly with the identical wire
encoding proven on mainnet by AerePQCAttestation
(`0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A`) and AerePQCKeyRegistry
(`0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`), and targets AereEntryPointV2
(`0x8D6f40598d552fF0Cb358b6012cF4227B86aF770`). Addresses are copied verbatim from
`sdk-js/src/addresses.ts`.
The precompile band, its activation block (9,189,161), and the wire encoding are verifiable
on `https://rpc.aere.network` (chain id 2800) as documented in AIP-7.
## Copyright
Released to the public domain (CC0). No rights reserved.

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 AERE Network
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

46
README.md Normal file
View File

@ -0,0 +1,46 @@
# aere-docs
The honest engineering and research documentation set for Aere Network.
This repository is the written half of the Aere Network "verify yourself" core. It collects the
protocol specification, the engineering and security spec, the EIP compatibility matrix, the
post-quantum cryptography and governance documents, the STARK verifier port specifications (with
their explicit caveats), the formal verification coverage reports, the reproducible-build and
cross-client determinism documents, and the reproducibility manifest.
## Start here
- `REPRODUCE.md` the single reproducibility manifest. It lists real paths, real commands, and
real expected outputs so a third party can re-run the tests, the formal models, the KATs, and the
benchmarks and get the same results. Every quantitative claim carries an honesty tag
(`[FRESH]`, `[CITED]`, or `[MEASURE]`).
- `AERE-PROTOCOL-SPECIFICATION.md` the protocol specification.
- `AERE-ENGINEERING-SECURITY-SPEC.md` the engineering and security spec.
- `AERE-ARCHITECTURE-MAP.md` the architecture map.
## Contents
- Compatibility and validation: `AERE-EIP-COMPATIBILITY-MATRIX.md`,
`AERE-NIST-VALIDATION-STATUS.md`, `AERE-CROSS-CLIENT-DETERMINISM.md`,
`AERE-REPRODUCIBLE-BUILDS.md`, `AERE-BENCHMARK-REPORT.md`.
- Post-quantum: `AERE-POST-QUANTUM-GOVERNANCE.md`, `AERE-QUANTUM-SAFE-NETWORKING.md`,
`AERE-QUANTUM-MIGRATION*.md`, `AERE-PQ-*.md`, `AERE-XMSS-*.md`, `AIP-PQ-TX-2026-07-18.md`.
- STARK verifier port: the `AERE-STARK-*.md` series, including
`AERE-STARK-DOC-CAVEAT-SWEEP.md` which records the Hypercube and other caveats plainly.
- Formal verification: `AERE-CERTORA-FORMAL-VERIFICATION.md`, `AERE-TLA-FORMAL-SPEC.md`,
`AERE-FORMAL-FINAL-COVERAGE.md`, `AERE-FORMAL-BROADEN-REPORT.md`,
`AERE-FORMAL-EXTENSION-MONEY-CONTRACTS.md`, `AERE-FAULT-INJECTION-HARNESS.md`.
- Security review: `AERE-SECURITY-REVIEW-NEW-CONTRACTS.md`, `AERE-SECURITY-FIXES.md`.
- Process and delivery: `AERE-AIP-PROCESS-AND-INDEX.md`, `AERE-NEW-CONTRACTS-DEPLOY-RUNBOOK.md`,
`AERE-DEPLOY-RUNBOOK-SUMMARY.md`, `AERE-BUILD-LOOP-SUMMARY-2026-07-19.md`.
## What is deliberately not here
Internal operations material, business and press strategy, infrastructure runbooks that name
hosts, funding and cost documents, and the mainnet-activation and node-provisioning runbooks are
intentionally excluded. Infrastructure IP addresses and host identifiers that appeared in a few
in-scope documents were redacted before staging.
## License
Documentation is provided under MIT. See `LICENSE`.

442
REPRODUCE.md Normal file
View File

@ -0,0 +1,442 @@
# REPRODUCE.md (spec-23): Verify Aere Network yourself
Prepared 2026-07-19. This is the single reproducibility manifest for Aere Network. It lets any
third party re-run our tests, our formal verification, our cryptographic known-answer tests (KATs),
and our benchmarks, and get the same results we get. Trust in a chain comes from reproducibility,
not from slogans, so this document is deliberately literal: real paths, real commands, real
expected outputs, and where a number cannot be freshly measured on a laptop, the exact command to
measure it rather than an invented figure.
## How to read the numbers in this file
Every quantitative claim below carries one of three honesty tags:
- **[FRESH]** means the number was produced by actually running the command on 2026-07-19 in the
environment described under Provenance. It is a real, re-observed result.
- **[CITED: file]** means the number is taken from a committed artifact in this repository, and
the file is named so you can open it and check. It was not re-derived from scratch for this
document, though where a suite was cheap to re-run its green status was reconfirmed [FRESH].
- **[MEASURE: command]** means the figure has not been measured here (the toolchain is not
installed on this machine, or the run is heavy or requires the isolated fork build). Run the
stated command to produce and publish the real number. Do not quote a figure until you have.
Nothing in this file is a rounded-up guess. If you find a bare number without a tag, treat it as a
bug in this document.
### Provenance of the fresh runs
- OS: Windows 11 (the SMT and Node suites are OS-independent; the fork KAT and gas harnesses are
Linux-only, see their sections).
- Python 3.14.4, z3 4.16.0 (`python -c "import z3; print(z3.get_version_string())"`).
- Node v24.14.1, Hardhat present in `aerenew/contracts/node_modules/.bin/hardhat`.
- `forge` (Foundry) and `halmos` are NOT installed on this machine, so every Foundry or Halmos
step below is tagged [MEASURE], never [FRESH].
## 1. What "verify yourself" means for Aere, and the honest scope
Aere's posture is **radical about reproducibility and surgical about secrets**. We publish the
things that let you check our claims: contract source, the full test suites, the formal
verification models, the NIST cryptographic vectors, and the benchmark harnesses. We never publish
the things that would let someone impersonate the network or move funds: validator or Foundation
private keys, infrastructure host addresses and access, internal business material, and the
frontier research and development we hold private. Those two lists do not overlap, and this
document only ever points at the first list.
Two things are true at once and we state both:
1. Aere mainnet (chain 2800) consensus is **classical secp256k1 ECDSA QBFT** (Hyperledger Besu
fork), N=7 Foundation validators (fault bound f=2, quorum 5 of 7), 0.5 second blocks. It is
NOT post-quantum. There is no Falcon certificate of any kind in any mainnet header.
2. The post-quantum work (Falcon log-only, blocking, and hybrid consensus modes) is isolated
testnet research and development, and five NIST PQC precompiles are live on mainnet at
`0x0AE1..0x0AE5` (from block 9,189,161) as an application-layer verification surface that does
not change how consensus itself is signed. See [CITED: aerenew/conformance/README.md] and
[CITED: aerenew/research-stage/formal-consensus/REPRODUCE.md] for the same boundary stated in
the source.
### The publish gate is mandatory (secret scan plus green CI)
No part of this bundle is published to a public remote until two gates pass, in this order:
- **Secret scan.** 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. See [CITED: aerenew/GITHUB_DEPLOYMENT_CHECKLIST.md].
- **Green CI.** Build, tests, and the secret scan run in CI, on a pull request, before merge.
Both gates are a hard prerequisite for step zero below. Preparing this manifest does not publish
anything.
## 2. PASUL 0 (state the honesty gap plainly): make "open source" true before claiming it
Today the site, SDK docs, and press material reference Aere source repositories that do not yet
resolve. This is a real over-promise and we name it rather than paper over it.
Confirmed references in the current tree:
- `aerenew/sdk-js/README.md` links to `https://git.aere.network/aere-network/sdk-js`,
`.../aere-network/aerenew`, and `.../aere-network/blocky-chain-forge`.
- `aerenew/tools/create-aere-pqc/README.md` links to `.../aere-network/sdk-js`.
- `aerenew/docs/aerecore/AERECORE_PLAN.md` links to `.../aere-network/aerecore`.
- The press kit and press draft link to `https://github.com/aerenetwork/aere-research`, and the
DevRel material references `aerenetwork/aere-docs` and `aerenetwork/aere-contracts`.
Reachability check on 2026-07-19: `curl -s -o /dev/null -w "%{http_code}"
https://git.aere.network/aere-network/aerenew` returned **404** [FRESH]. Until the referenced
repositories are actually published, the "open source" and "verify yourself" claims over-promise.
[VERIFY: individually curl each referenced repo path (git.aere.network/aere-network/{sdk-js,
aerenew,blocky-chain-forge,aerecore} and github.com/aerenetwork/{aere-research,aere-docs,
aere-contracts}); the aerenew path is confirmed 404, the rest are asserted from the same host and
should be checked one by one before the site copy is trusted.]
**Step zero of this bundle is to make the claim true**: publish the real repositories, behind the
secret scan plus green CI gate of section 1. Publishing itself is gated on clean accounts and is a
manual step. This document does not perform it, and nothing here should be read as having pushed
anything.
## 3. Reproduction sections (one per verifiable artifact)
### 3a. Contract test suites
**What it proves.** That the deployed Solidity behaves as claimed: PQC verifiers accept genuine
signatures and reject tampered ones, escrow and custody accounting conserves funds, and the
economics contracts have no admin escape hatch.
**Primary functional surface: Hardhat (JavaScript).** The functional test suite is Hardhat, not
Foundry. It lives at `aerenew/contracts/test/*.test.js`.
```bash
cd aerenew/contracts
npm ci # installs the pinned toolchain (hardhat, OZ 4.9.x)
npx hardhat test # runs the full suite
```
- Test file count: **137** files matching `test/*.test.js` [FRESH]
(`find test -name '*.test.js' | wc -l` = 137).
- Of those, **22** are invariant-property fuzz suites over burn, economics, and custody contracts
[FRESH] (19 named `*-invariant-property.test.js` plus 3 named `invariant-*-property.test.js`;
this matches the "22 invariant-property fuzz suites" figure in
[CITED: aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md]).
- Spot-check actually run here: `npx hardhat test test/AereCryptoRegistry.test.js` returned
**42 passing (3s)**, exit 0 [FRESH] (artifacts were already compiled in `contracts/artifacts`,
so no solc download was needed). This confirms the harness runs and a representative file is
green on this machine.
- Full-suite pass count: [MEASURE: run `npx hardhat test` and publish the "N passing" line. Do
not restate an aggregate here until the whole suite has been run in one invocation. An earlier
memory note cites "161/161" from a specific historical milestone; that number is NOT re-verified
by this document and must not be presented as the current full-suite result without a fresh run.]
**Symbolic layer: Foundry plus Halmos.** A `foundry.toml` sits beside the Hardhat project,
deliberately scoped (its own `out-forge`/`cache-forge`, `src = contracts/pqc`, `test =
test/formal`) so it does not disturb Hardhat. It exists for symbolic execution over three PQC
contracts: `test/formal/AereCryptoRegistry.symbolic.t.sol`,
`AerePQCAttestation.symbolic.t.sol`, and `AereSpokePool.symbolic.t.sol`.
```bash
cd aerenew/contracts
forge test # compiles contracts/pqc against out-forge
halmos # symbolic execution of the *.symbolic.t.sol properties
```
[MEASURE: `forge` and `halmos` are not installed on the machine that produced this document. Run
the two commands above and publish (a) the forge pass/fail summary and (b) the halmos per-property
result. The security spec references "15 symbolic-execution proofs over the core contracts"
[CITED: aerenew/docs/drafts/PRESS-RELEASE-DRAFT-2026-07-18.md]; treat 15 as a documented claim to
be reconfirmed by a fresh halmos run, not as a freshly measured number.]
### 3b. Formal verification (z3 SMT, plus Halmos and Solidity SMTChecker)
**What it proves.** Safety-critical properties are machine-checked, not argued in prose. Each
property P is proved by asserting NOT P under the exact protocol guards and showing z3 returns
UNSAT (no counterexample). Every proof is paired with a **negative control**: the same model with
one guard removed, which must return SAT (a real counterexample). If a control ever failed to
fire, the model would be suspect. A green run is therefore both correct and demonstrably
non-vacuous.
**Consensus suite, publish target (10 models).** Location:
`aerenew/research-stage/formal-consensus/` (curated copy prepared for the public research repo,
with `REPRODUCE.md` and a committed `expected-output.log`).
```bash
pip install z3-solver
cd aerenew/research-stage/formal-consensus
python run_consensus_verification.py
```
Fresh result on 2026-07-19 [FRESH]:
```
models run: 10 passed: 10
RESULT: ALL MODELS PASS (proofs PROVED, negative controls fired)
```
Counts from the fresh run: **173 PROVED verdict lines, 121 CEX-FOUND (negative-control) verdict
lines, 0 FAILED**, exit 0 [FRESH]. These verdict-line counts include both the per-check line and
its echo in each model's SUMMARY block, so they are stable verdict counts rather than a count of
distinct unique properties; they match the committed reference in
[CITED: aerenew/research-stage/formal-consensus/REPRODUCE.md] exactly (that file records the same
173 PROVED / 121 CEX-FOUND / 0 FAILED under z3 4.16.0, Python 3.14.4). The ten models are
qbft_safety, qbft_liveness, qbft_locking, qbft_prepare_counting, qbft_digest_keyed,
falcon_logonly_noop, falcon_blocking, qbft_pqc_activation, falcon_hybrid_dualquorum, and
threshold_account.
**Consensus + contract suite, canonical in-tree (19 models: 8 consensus + 11 contract).** The
non-staging copy at `aerenew/formal-consensus/run_consensus_verification.py` now wires the 8 QBFT /
Falcon consensus models AND 11 contract SMT models (fund-flow, registry, and PQC-verifier
invariants), so a single canonical run covers them and prints a verdict-line tally. The 11 contract
models are `computemarket_smt`, `destinationsettler_smt`, `migrator_smt` (escrow / filler / no-custody
fund-flow), `pqfinality_smt` (Post-Quantum Finality Certificate accept guards: quorum ceil(2N/3),
validator-set-root / size / attestation-domain binding, and rotation-invalidates-old-root),
`pqaggregate_smt` (constant-cost t-of-n post-quantum aggregate authorization: committee-root and
message-digest binding), `credential_registry_smt` (federated compliance trust: accreditation-to-issue,
append-only status history, fail-closed verification), `recovery_registry_smt` (Falcon-512 append-only,
strictly-increasing-nonce, fail-closed recovery evidence), plus the four added in this loop:
`ap2_mandate_smt` (AereAP2MandateVerifier spend-authorization: cumulative spend stays within the
mandate cap inductively, an expired or not-yet-valid mandate authorizes nothing, and the post-fix L3
executor gate blocks the open replay path), `reputation_gate_smt` (AereReputationRegistry8004 M1 fix:
only an owner-authorized feedback author can move a score, and the forwarded delta is clamped to
{-1,0,+1}), `vectorstore_smt` (AereVectorStore: version strictly increases by one per commit, the
version history is append-only, every retrieval names a committed version 1..current, and a priced
receipt is consumed at most once), and `bitstring_status_smt` (AereBitstringStatusList: a Revocation
list's status bit is monotone / terminal so a revoked credential can never be un-revoked, the epoch
strictly increases, and setting one bit leaves every other credential's bit unchanged). The runner
still omits the hybrid dual-quorum and threshold consensus models from its list, though those `.py`
files are present in the same directory and are run under the staging suite above.
```bash
cd aerenew/formal-consensus
python run_consensus_verification.py
```
Fresh result on 2026-07-19 [FRESH]: exit 0, all **19 of 19 models PASS** (8 consensus + 11 contract),
`RESULT: ALL MODELS PASS (proofs PROVED, negative controls fired)`. The runner's own verdict-line
tally on that run was **156 PROVED, 89 CEX-FOUND, 0 FAILED** [FRESH]. That tally counts each model's
per-check line once (the `[PROVED ...]` / `[CEX-FOUND ...]` lines), so it is a count of distinct
property checks (proofs plus their firing negative controls), not of SUMMARY echoes. Of that total
the four models added in this loop contributed **28 PROVED / 10 CEX-FOUND** (ap2_mandate 9/3,
reputation_gate 5/2, vectorstore 8/3, bitstring_status 6/2); the earlier four registry / PQC-verifier
models contributed **29 PROVED / 10 CEX-FOUND** (pqfinality 10/3, pqaggregate 7/2, credential_registry
7/3, recovery_registry 5/2); the three fund-flow models **26 PROVED / 11 CEX-FOUND** (computemarket
16/3, destinationsettler 5/4, migrator 5/4); and the 8 consensus models account for the remaining 73
PROVED / 58 CEX-FOUND. Every contract model follows the same discipline as the rest of the corpus:
each 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 (proving non-vacuity).
Each contract model states its DESIGN-level boundary and its [VERIFY] trusted primitives honestly
(for the PQC verifiers, SP1 gateway soundness and the un-implemented [MEASURE] off-chain zkVM
aggregation circuit; for the Falcon-512 paths, the live precompile at 0x0AE1 and keccak256
injectivity; for the ERC-8004 mandate / reputation adapters, that the model bounds the adapter's
guard logic and accounting, not the AERE402 rail's token movement or the live reputation's score
formula).
**Contract SMT models (9 models, escrow / custody / registry invariants).** Same directory,
`aerenew/formal-consensus/`. Each is a standalone z3 script.
```bash
cd aerenew/formal-consensus
for m in agentdid_lifecycle_smt agentdid_session_smt cryptoregistry_smt hybridauthorizer_smt \
lending_liquidation_smt pqckeyregistry_smt settlementhub_smt spokepool_smt \
threshold_account_smt; do python "$m.py" || echo "FAILED $m"; done
```
Fresh result on 2026-07-19 [FRESH]: all **9 of 9 models exit rc=0** (green). Per-model exit codes
were 0 for agentdid_lifecycle, agentdid_session, cryptoregistry, hybridauthorizer,
lending_liquidation, pqckeyregistry, settlementhub, spokepool, threshold_account.
The aggregate property count for these 9 contract models is **125 proved properties plus 49
counterexample negative controls** [CITED: aerenew/docs/SECURITY-POSTURE-VERIFICATION-2026-07-15.md
(line 171), also aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md]. The per-model breakdown recorded
in that doc is agentdid_lifecycle 15, agentdid_session 11, cryptoregistry 12, hybridauthorizer 17,
lending_liquidation 11, pqckeyregistry 13, settlementhub 15, spokepool 26, threshold_account 5,
which sums to 125. The two highest-risk fund-flow models are the load-bearing ones:
`settlementhub_smt.py` proves `balanceOf(hub) >= committedLiabilities` inductively, and
`spokepool_smt.py` proves `bal >= totalLocked + totalBond` inductively.
[MEASURE: to publish the exact fresh per-model property counts rather than the cited 125/49, run
each model and read the SUMMARY block; the counting convention varies per model (some checks are
parameterized over several validator-set sizes), so re-derive the aggregate with a single agreed
counting rule before restating 125/49 as a freshly measured figure. The green (rc=0) status of all
nine is freshly confirmed above.]
**Optional secondary cross-check (not a proof).** A Quint spec `FalconQuorum.qnt` is a randomized
simulation used only as an independent sanity check:
```bash
npm i -g @informalsystems/quint # reference version 0.32.0
quint run FalconQuorum.qnt --main=n7 --invariant=safety --max-samples=20000 --max-steps=1
```
**Halmos and Solidity SMTChecker** over the contracts are the symbolic complement to the z3
design-level models; see section 3a for the Halmos command and its [MEASURE] tag.
**Honest boundary for all SMT models.** The quorum-intersection lemma, the blocking-liveness
identity, and the threshold arithmetic are proved for all N via decidable linear integer
arithmetic. The one-round, cross-round, safety, and progress checks are bounded model checks over
finite configurations (mainly N in {4,5,7}). A bounded model check is a decision procedure over
that finite configuration, not a proof for all N or all execution lengths. Liveness assumes
partial synchrony (post-GST), which is standard for QBFT and forced by FLP. These models check the
protocol design mathematics, not the compiled client bytecode. Stated plainly, per
[CITED: aerenew/research-stage/formal-consensus/REPRODUCE.md].
### 3c. PQC KAT / ACVP harness
**What it proves.** That the PQC verifier implementations accept the official NIST vectors and
reject crafted-invalid inputs, so "NIST-conformant" is a checkable statement rather than a label.
**Contract-level KATs (run under Hardhat, no fork build needed).** The committed vectors live in
`aerenew/contracts/test/` and `aerenew/contracts/test/fixtures/`:
| Scheme | Vector file | Committed conformance |
|---|---|---|
| Falcon-512 | `test/falcon512_kat0.json` | official NIST round-3 KAT, vector 0 [CITED: test/AereFalcon512Verifier.test.js] |
| Falcon-1024 | `test/falcon1024_kat0.json` | official round-3 KAT |
| ML-DSA-44 (Dilithium2, FIPS 204) | `test/fixtures/mldsa44-acvp-tg8.json` | 15/15 NIST ACVP sigVer cases [CITED: aerenew/aips/AIP-4.md] |
| SLH-DSA / SPHINCS+-SHA2-128s (FIPS 205) | `test/fixtures/sphincs-sha2-128s-acvp-tg31.json` | 14/14 NIST ACVP sigVer cases [CITED: aerenew/aips/AIP-4.md] |
| XMSS-SHA2_10_256 (RFC 8391) | `test/fixtures/xmss-sha2_10_256-kat.json` | RFC 8391 KAT |
```bash
cd aerenew/contracts
npx hardhat test test/AereFalcon512Verifier.test.js # SHAKE256, HashToPoint, accept + tamper-reject
npx hardhat test test/AereFalcon1024Verifier.test.js
npx hardhat test test/AereSphincsVerifier.test.js
```
The Falcon-512 test asserts, among others, that the FIPS 202 SHAKE256 empty-string vector
reproduces `0x46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f`, that HashToPoint
reproduces the reference challenge polynomial, and that a genuine signature accepts while a
tampered one rejects. [MEASURE: run the three commands above and publish each "N passing" line.
Only the CryptoRegistry file was executed for this document (42 passing, 3a).]
**Fork-level precompile KATs (require the isolated Besu PQC fork build, Linux).** These feed the
committed NIST ACVP vectors to the native precompiles and assert accept plus crafted-invalid
reject. Location `aerenew/pqc-fork/`.
```bash
cd aerenew/pqc-fork
bash setup-fork.sh # clone besu@d203201, overlay fork EVM layer, add precompiles, compile
bash build-dist.sh # ./gradlew installDist (one full build)
bash run-kats.sh # ML-KEM-768 (NIST ACVP) + Falcon HashToPoint (Python SHAKE256 cross-check)
```
Committed results, to be reconfirmed by a fresh run:
- ML-KEM-768 (FIPS 203) at `0x0AE6`: **25/25 NIST ACVP** encapsulation cases PASS
[CITED: aerenew/pqc-fork/results/kat-results-mlkem.json].
- Falcon HashToPoint (SHAKE256 rejection sampler) at `0x0AE7`: **12/12** PASS, cross-checked
against an independent Python `hashlib.shake_256` oracle
[CITED: aerenew/pqc-fork/results/kat-results-hashtopoint.json].
[MEASURE: `run-kats.sh` targets `/root/besu` and `/root/staging` on the isolated fork host and
cannot run on this machine or on the live infra box (heavy build; the infra box runs live services
and must never take heavy compute). Reproduce on a scratch Linux box and publish the two result
JSONs. The Rust acceleration crate has its own KATs: `cd aerenew/pqc-accel && cargo test`
(integration tests `slhdsa_kat`, `scalar_hash_kat`); [MEASURE: run cargo test and publish the
result, cargo is not exercised here].]
### 3d. Benchmark harness (gas)
**What it proves.** What each signature scheme actually costs on-chain, and how much the native
precompile saves versus a pure-Solidity verifier.
**Precompile gas schedule (marginal verify-op gas).** These are the per-operation gas charges for
the native precompiles, defined in the fork source and specified in
[CITED: aerenew/aips/AIP-7.md] and [CITED: aerenew/conformance/README.md]:
| Scheme | Marginal verify-op gas | Full verify-and-record tx gasUsed (measured on fork) | Share of 2^24 cap |
|---|---|---|---|
| ecrecover (secp256k1) | 3,000 (protocol standard) | n/a | n/a |
| P-256 / secp256r1 (RIP-7212/7951 at `0x100`) | 3,450 | n/a | n/a |
| Falcon-512 | 40,000 | 86,336 | 0.51% |
| Falcon-1024 | 75,000 | 145,496 | 0.87% |
| ML-DSA-44 | 55,000 | 351,050 | 2.09% |
| SLH-DSA-SHA2-128s | 350,000 | 558,276 | 3.33% |
| ML-KEM-768 encapsulate (`0x0AE6`) | 60,000 | n/a | n/a |
| SHAKE256 / HashToPoint (`0x0AE7`) | 60 base + 12/word | n/a | n/a |
Sources: the marginal and full-tx columns are [CITED: aerenew/aips/AIP-7.md] (table at lines
92-98); ecrecover 3,000 is [CITED: aerenew/docs/AERE-ENGINEERING-SECURITY-SPEC.md]; P-256 3,450 is
[CITED: aerenew/aips/AIP-6.md and aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md]; the ML-KEM 60,000
and HashToPoint 60-base constants are read directly from the precompile source
[CITED: aerenew/pqc-fork/precompiles/MLKEM768PrecompiledContract.java (GAS = 60_000L) and
HashToPointPrecompiledContract.java (BASE_GAS = 60)]. Honest caveat from
[CITED: aerenew/conformance/README.md]: precompile gas is documented in the fork source but the
conformance vectors pin outputs, not gas schedules, so gas conformance is a stated next addition.
[MEASURE: the full verify-and-record gasUsed figures (86,336 / 145,496 / 351,050 / 558,276) were
measured once on the isolated fork and committed to the AIP. To reproduce them freshly, build the
fork (section 3c) and run a verify-and-record transaction per scheme through the live precompiles,
then publish the receipt gasUsed. Do not restate these as current unless re-measured.]
**Precompile-versus-Solidity Falcon-512 gas benchmark.** `aerenew/pqc-fork/bench/bench.py` deploys
the pure-Solidity verifier and the precompile-backed variant on a single-node QBFT fork, runs the
same Falcon-512 KAT vectors through both, and reports receipt gasUsed and accept/tamper-reject.
```bash
cd aerenew/pqc-fork
bash bench/start-node.sh
python3 bench/bench.py
```
Committed result [CITED: aerenew/pqc-fork/results/bench-results.json], 8 vectors,
`integration_all_pass: true`, chainId 28777:
- Average in-EVM Falcon-512 verify: **9,140,858 gas**, precompile-backed: **7,186,367 gas**, a
**21.4%** drop.
- HashToPoint step alone: **2,281,180** to **311,480 gas**, roughly an 86% drop.
[MEASURE: bench.py assumes the fork node at 127.0.0.1:8545 and `solc` on PATH; it is Linux/fork
tooling and was not run here. Rebuild the fork, run it, and publish the fresh bench-results.json.]
**Chain and zkVM benchmarks.** The 273,000 TPS figure is a design ceiling from synthetic
benchmarks, not a measured mainnet steady-state number, and must be stated that way if it appears
at all [CITED: aerenew/GITHUB_DEPLOYMENT_CHECKLIST.md]. [MEASURE: any chain-throughput or zkVM
proving-time number belongs here only with the harness command and a fresh run; none is asserted
in this document.]
## 4. Coverage and mutation
Neither of these is a claimed number. Both are commands to run and publish.
**Coverage.**
```bash
cd aerenew/contracts
npx hardhat coverage # solidity-coverage, the Hardhat-native coverage path
# or, once the Foundry profile is used for the pqc/ scope:
forge coverage
```
[MEASURE: run one of the above and publish the real line/branch coverage report. Do NOT write a
coverage percentage in any public copy until it comes from a fresh run of this command. A
fabricated "99.8% coverage" is a catastrophic credibility failure; an honest "here is the command,
here is the report" is the standard.]
**Mutation testing.**
```bash
# Solidity mutation testing, e.g. a Gambit or vertigo-style run over contracts/contracts,
# scored against the Hardhat suite:
# (tool not pinned in-repo yet) install the chosen mutation tool, then run it against the suite.
```
[MEASURE: select and pin a mutation-testing tool, run it against the Hardhat suite, and publish the
mutation score (killed / total). No mutation number is claimed here.]
## 5. What is NOT in this bundle
By design, this reproducibility bundle excludes:
- **Keys.** No validator or Foundation private keys, no signing material, no `.env` with real
values, no API tokens. Deployed contract addresses are public and checkable via `eth_getCode` on
`https://rpc.aere.network`; the keys that control them are not here and never will be.
- **Infrastructure.** No host addresses, SSH access, nginx or firewall configuration, relayer,
oracle, or indexer operational material. The live infra box runs production services and must
never take heavy compute; every heavy harness in this document is explicitly routed to a scratch
box or the fork host, not to infra.
- **Business material.** No funding, cost, partner, or internal-operations content. Public copy is
scrubbed of it as a matter of policy.
- **Frontier research and development held private.** Only the "verify-yourself" core (already
public or intended for the public research repo) is in scope. Private frontier work stays
private; the moat is the live network, not the unpublished code.
Publishing any of section 3's artifacts remains gated on the secret scan plus green CI of section
1, and on the account-readiness that gates step zero of section 2. This document prepares the
verification story; it does not push anything.