aere-docs/AERE-ENTROPY-BEACON-FIX.md
Aere Network e4cead319d 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.
2026-07-20 01:01:36 +03:00

126 lines
6.4 KiB
Markdown

# 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.