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.
256 lines
12 KiB
Markdown
256 lines
12 KiB
Markdown
# aere-node
|
|
|
|
Post-quantum signature verification precompiles for Hyperledger Besu, as they run on Aere Network
|
|
mainnet (chain 2800).
|
|
|
|
This repository is meant to be used, not read. It contains the precompile sources, a patch that
|
|
applies to a named upstream Besu commit, the NIST known-answer vectors, and a script that checks the
|
|
live chain without asking you to trust anything here.
|
|
|
|
---
|
|
|
|
## Scope boundary, stated first and not in a footnote
|
|
|
|
**These precompiles verify post-quantum signatures inside the EVM. Consensus on chain 2800 is
|
|
classical secp256k1 ECDSA QBFT.**
|
|
|
|
Aere Network does not have post-quantum consensus. Block sealing, validator identity and the QBFT
|
|
vote messages are all classical elliptic-curve cryptography today, and a cryptographically relevant
|
|
quantum computer would break them exactly as it would break any other ECDSA chain. What is live and
|
|
post-quantum is the *verification* capability exposed to the EVM: a contract or an account
|
|
abstraction wallet on chain 2800 can verify a Falcon, ML-DSA or SLH-DSA signature natively, at
|
|
precompile cost, today.
|
|
|
|
Two further boundaries in the same spirit:
|
|
|
|
- The on-chain zero-knowledge verifiers used elsewhere in the Aere stack are classical BN254 pairing
|
|
verifiers. They are Shor-breakable. They are not quantum-safe and are never described as such.
|
|
- `0x0AE6` and `0x0AE7` are **testnet-only**. They are not on mainnet. The verification script in
|
|
this repository asserts their absence from mainnet as a falsifiable check, so that a reader can
|
|
catch us if that ever stops being true.
|
|
|
|
---
|
|
|
|
## The precompiles
|
|
|
|
Activated on mainnet chain 2800 at block **9,189,161**.
|
|
|
|
| Address | Algorithm | Standard | Input framing | Output |
|
|
|---|---|---|---|---|
|
|
| `0x0AE1` | Falcon-512 verify | NIST Falcon round 3 | `pk \|\| sm` | 32-byte word, 1 accept / 0 reject |
|
|
| `0x0AE2` | Falcon-1024 verify | NIST Falcon round 3 | `pk \|\| sm` | 32-byte word, 1 accept / 0 reject |
|
|
| `0x0AE3` | ML-DSA-44 verify | FIPS 204 | `pk \|\| sm` | 32-byte word, 1 accept / 0 reject |
|
|
| `0x0AE4` | SLH-DSA-SHA2-128s verify | FIPS 205 | `pk \|\| sm` | 32-byte word, 1 accept / 0 reject |
|
|
| `0x0AE5` | SHAKE256 XOF | FIPS 202 | 32-byte big-endian `outLen` word, then data | `outLen` bytes |
|
|
|
|
`sm` is the NIST signed-message convention: `signature || message`.
|
|
|
|
Not part of this fork but relevant and also live: `0x100` P-256 verify (RIP-7951), which is
|
|
classical.
|
|
|
|
**Testnet only, not on mainnet:**
|
|
|
|
| Address | Algorithm | Standard | Status |
|
|
|---|---|---|---|
|
|
| `0x0AE6` | ML-KEM-768 deterministic encapsulation | FIPS 203 | Built, 25/25 NIST ACVP KAT on an isolated single-validator QBFT testnet |
|
|
| `0x0AE7` | Falcon HashToPoint (SHAKE256 rejection sampler) | NIST Falcon round 3 | Built, 12/12 KAT on the same isolated testnet |
|
|
|
|
Mainnet activation of either is a governance decision that has not been taken.
|
|
|
|
The Java sources for all seven are in `precompiles/`, and the same sources are what the patches in
|
|
`patches/` install.
|
|
|
|
---
|
|
|
|
## The patch
|
|
|
|
Upstream Hyperledger Besu is roughly a million lines. Our contribution is a few hundred. Publishing a
|
|
full fork tree would bury the contribution in upstream code and leave you unable to tell our lines
|
|
from Besu's without diffing it yourself. A patch against a named upstream commit inverts that: the
|
|
diff *is* the contribution, you fetch the other 99 percent from Hyperledger directly, and when
|
|
upstream moves we rebase rather than merge a divergent tree forever.
|
|
|
|
Both patches target upstream commit `d2032017bb3b8cb215a97303980a1e4a643f7180`, authored
|
|
2026-04-17. That is the same commit named in the artifact running on the validators,
|
|
`besu-evm-26.7-develop-d203201.jar`.
|
|
|
|
| Patch | Adds | Applies to |
|
|
|---|---|---|
|
|
| `patches/0001-aere-pqc-precompiles-mainnet.patch` | `0x0AE1` to `0x0AE5`, the five live on mainnet | pristine upstream `d2032017` |
|
|
| `patches/0002-aere-pqc-precompiles-testnet.patch` | `0x0AE6` and `0x0AE7`, testnet only | the tree after 0001 |
|
|
|
|
Between them they touch eight files: two upstream files modified (`Address.java` for the address
|
|
constants, `MainnetPrecompiledContracts.java` for the registry wiring) and six new precompile
|
|
classes plus one shared Falcon helper. **No build file changes are required.** Bouncy Castle
|
|
`bcprov-jdk18on` 1.83 is already on the `evm` module's compile classpath as an `api` dependency of
|
|
`crypto:algorithms`, and `jakarta.validation-api` is a global subproject dependency.
|
|
|
|
Patch 0001 also repoints `populateForFutureEIPs` from `populateForCancun` to `populateForOsaka`, so
|
|
the fork carries the full Osaka precompile set. That is a real behaviour change beyond the five
|
|
precompiles and is called out here rather than left to be discovered in the diff.
|
|
|
|
### Apply it yourself
|
|
|
|
```
|
|
git clone --filter=blob:none https://github.com/hyperledger/besu.git
|
|
cd besu
|
|
git checkout d2032017bb3b8cb215a97303980a1e4a643f7180
|
|
git apply --check /path/to/aere-node/patches/0001-aere-pqc-precompiles-mainnet.patch
|
|
git apply /path/to/aere-node/patches/0001-aere-pqc-precompiles-mainnet.patch
|
|
```
|
|
|
|
Add `0002` the same way if you want the testnet pair as well. Both are `git format-patch` output, so
|
|
`git am` works too and carries the commit message.
|
|
|
|
**Verified, 2026-07-20.** Both patches were applied to a freshly fetched, byte-clean checkout of
|
|
`d2032017` in a scratch directory. `git apply --check` and `git apply` each returned exit 0 for both
|
|
patches, and the resulting files are byte-identical to the sources in `precompiles/`.
|
|
|
|
> A note on how that verification earned its place. An earlier draft of patch 0001 also returned
|
|
> exit 0 from `git apply --check`, and it was still wrong: it registered a helper class,
|
|
> `AereFalconSupport`, that it never added, so it applied cleanly and then failed to compile. A patch
|
|
> that applies is not a patch that builds. That is why the next section exists.
|
|
|
|
---
|
|
|
|
## Build
|
|
|
|
```
|
|
./gradlew :evm:compileJava # the module this patch touches
|
|
./gradlew installDist # full node distribution, in build/install/besu/
|
|
```
|
|
|
|
Requires JDK 21. Commit `d2032017` sits on the development line after the 26.4.0 release, which is
|
|
the newest heading in that commit's `CHANGELOG.md`. The artifact running on the validators is named
|
|
`besu-evm-26.7-develop-d203201.jar`; treat the commit hash, not the version string, as the identifier
|
|
that matters.
|
|
|
|
Note that upstream has since moved from `hyperledger/besu` to `besu-eth/besu`. GitHub redirects the
|
|
old URL, so the clone command above still resolves, and it was the exact command used for the
|
|
verification below.
|
|
|
|
**Verified, 2026-07-20.** `./gradlew --no-daemon :evm:compileJava` was run on the pristine
|
|
`d2032017` checkout with both patches applied, on JDK 21.0.11. It exited 0. Ten class files were
|
|
emitted for the seven precompiles and the Falcon helper, including the nested `InternalVerifier`
|
|
classes of `MLDSA44PrecompiledContract` and `SLHDSA128sPrecompiledContract`. The only compiler notes
|
|
were pre-existing upstream deprecation warnings in `datatypes/.../Log.java`, unrelated to this patch.
|
|
|
|
`./gradlew installDist` produces the full node distribution but was **not run here**, so this
|
|
repository does not claim it. A reader who wants the whole node builds it themselves with the command
|
|
above; the module this patch actually touches is `:evm`, and that is what was compiled.
|
|
|
|
### One rough edge, stated rather than hidden
|
|
|
|
`./gradlew build` will stop at Besu's formatter gate. `:evm:spotlessJavaCheck` and
|
|
`:datatypes:spotlessJavaCheck` both exit 1 on our files. The violations are entirely cosmetic and
|
|
fall into three groups: Javadoc rewrapped at a different column, the seven `AERE_*` address constants
|
|
in `Address.java` exceeding the 100-column limit on one line, and Besu's license-header rule wanting
|
|
`Copyright contributors to Besu` where our files say `Copyright contributors to the AERE Network`.
|
|
|
|
Nothing there changes behaviour, and `:evm:compileJava` passes as reported above. We have not
|
|
reformatted, for two reasons. Running `spotlessApply` would rewrite our copyright attribution to
|
|
Besu's, which would be wrong on files we wrote. And the sources in this repository are byte-identical
|
|
to the sources that built the artifact running on the validators; reformatting them for a cleaner
|
|
`gradlew build` would trade that property away for cosmetics.
|
|
|
|
To build past it:
|
|
|
|
```
|
|
./gradlew installDist -x spotlessJavaCheck
|
|
```
|
|
|
|
Or run `./gradlew spotlessApply` first if you would rather have upstream formatting and do not mind
|
|
the header rewrite.
|
|
|
|
Registration happens in `MainnetPrecompiledContracts.populateForFutureEIPs`, which is why the
|
|
precompiles are gated by fork activation rather than present from genesis.
|
|
|
|
---
|
|
|
|
## Check the result against the live chain
|
|
|
|
You do not need our source, our binary or our word for this part. The five precompiles are
|
|
addressable on public mainnet RPC. Point NIST's own published test vectors at them and read what
|
|
comes back.
|
|
|
|
```
|
|
node scripts/verify-live-precompiles.mjs
|
|
```
|
|
|
|
Requires Node 18 or later. It sends only `eth_call`, `eth_chainId`, `eth_blockNumber` and
|
|
`eth_getCode`. It sends no transaction, signs nothing and holds no key. Override the endpoint with
|
|
`AERE_RPC=https://rpc2.aere.network` for a second, independently served view of the chain.
|
|
|
|
Thirteen checks run, and every signature check is paired with a negative control. A precompile that
|
|
simply returned "valid" for all input would pass the positive check and fail the tampered one.
|
|
|
|
**Output of the run staged with this README (MEASURED, 2026-07-20):**
|
|
|
|
```
|
|
RPC: https://rpc.aere.network
|
|
chainId: 0xaf0 (2800)
|
|
block: 0xa13c28 (10566696)
|
|
|
|
PASS 0x0AE5 SHAKE256(abc) outLen=32 vs FIPS 202
|
|
PASS 0x0AE5 SHAKE256(empty) outLen=32 vs FIPS 202
|
|
PASS 0x0AE5 SHAKE256(abc) outLen=64 (true XOF squeeze)
|
|
PASS 0x0AE1 Falcon-512 official NIST KAT vector 0 ACCEPTS
|
|
PASS 0x0AE1 Falcon-512 tampered signed-message REJECTS (negative control)
|
|
PASS 0x0AE2 Falcon-1024 official NIST KAT vector 0 ACCEPTS
|
|
PASS 0x0AE2 Falcon-1024 tampered signed-message REJECTS (negative control)
|
|
PASS 0x0AE3 ML-DSA-44 ACVP tc108 expectedPass=true
|
|
PASS 0x0AE3 ML-DSA-44 ACVP tc106 expectedPass=false
|
|
PASS 0x0AE4 SLH-DSA-128s ACVP tc422 expectedPass=true
|
|
PASS 0x0AE4 SLH-DSA-128s ACVP tc421 expectedPass=false
|
|
PASS 0x0AE6 ML-KEM-768 is NOT live on mainnet 2800 (docs say testnet-only)
|
|
PASS 0x0AE7 Falcon HashToPoint is NOT live on mainnet 2800 (docs say testnet-only)
|
|
|
|
13/13 checks passed
|
|
```
|
|
|
|
The script prints expected and actual bytes for every check, which the summary above elides. The
|
|
vectors it uses are in `vectors/`: official NIST Falcon KAT vector 0 for both parameter sets, and
|
|
NIST ACVP fixtures for ML-DSA-44 and SLH-DSA-SHA2-128s.
|
|
|
|
Chaining the three sections gives the property this repository is for. The patch applies to an
|
|
upstream commit you fetch from Hyperledger. The result compiles. The compiled precompiles answer the
|
|
NIST vectors the same way the addresses on chain 2800 answer them.
|
|
|
|
---
|
|
|
|
## Correction, 2026-07-20
|
|
|
|
An earlier version of this README stated that the Java sources for the five precompiles live on
|
|
mainnet were unrecoverable, and it built its whole structure around that gap. **That was wrong**, and
|
|
the correction is kept here rather than quietly edited away.
|
|
|
|
All five exist, complete, on the infrastructure host, alongside five full Besu source trees and a
|
|
backup tarball. The earlier search looked in `/root` and at shallow depths under `/opt`, and never
|
|
opened the directory named after the thing it was looking for.
|
|
|
|
They are the real sources, not a lookalike. Three independent checks support that: they declare
|
|
exactly the live addresses `0x0AE1` through `0x0AE5`; the tree sits on upstream Besu commit
|
|
`d2032017`, the same commit named in the shipped artifact `besu-evm-26.7-develop-d203201.jar`; and
|
|
the source timestamps precede the jar build, as they must.
|
|
|
|
The lesson is worth more than the scare. A negative result about your own infrastructure is only as
|
|
good as the paths that were searched, and here "I did not find it" was reported as "it does not
|
|
exist". Every search of that kind on this project now has to state where it looked.
|
|
|
|
---
|
|
|
|
## What is deliberately not here
|
|
|
|
No validator keys, no node keys, no enode URLs, no `static-nodes.json`, no operational configuration,
|
|
no server addresses. Aere Network publishes its code and not its keys, which is the same line Linux
|
|
draws. A secret scanner gates every file in this directory before it is pushed anywhere.
|
|
|
|
Genesis and chain configuration for joining chain 2800 as a full node are a separate concern from
|
|
this repository and are documented with the network's node operator material.
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
Apache 2.0, matching upstream Hyperledger Besu. See `LICENSE`.
|