aere-docs/AERE-ENTROPY-BEACON-FIX.md
Aere Network dd1410c266 The unpublished line of work joins the sanitized public line
The published line and the local line of this repository had no common
ancestor: the public one carried the hygiene pass (no host names, no internal
paths), the local one carried a month of corrections that never shipped. This
commit ports the local work onto the public line, keeping the public hygiene
wording wherever the two touched the same sentence, and keeping the public
version of AERE-CROSS-CLIENT-DETERMINISM.md entirely.

Carried: LICENSE/LICENSING corrections, VERIFY-POLICY.md,
CITATIONS-UNRESOLVED.md remeasured 2026-08-11, the 'audited' adjective removed
from next to Bouncy Castle, citation paths rewritten to published form, AIP-8,
the QA consolidation report, the second EIP validation pass, fork-height
corrections, the AereSink / threshold-factory correction, the forge test
floor, and the architecture-map updates.
2026-08-15 13:54:42 +03:00

126 lines
6.4 KiB
Markdown

# Spec 9: Aere entropy honesty fix, real drand BLS verification
## The gap (what was dishonest)
`aere-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):
- `aere-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.
- `aere-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.