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.
6.4 KiB
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:
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.sollibrary AereDrandQuicknetBLS, real BLS12-381 verification on EIP-2537:messageForRound=SHA-256(round as 8-byte big-endian)(quicknet is UNCHAINED)hashToField= RFC 9380expand_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 viaBLS12_MAP_FP_TO_G1(0x10) +BLS12_G1ADD(0x0b). (map already clears the cofactor, and cofactor clearing is a homomorphism, somap(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 checke(H(m),pk) * e(sig,-G2) == 1viaBLS12_PAIRING_CHECK(0x0f).
contract AereRandomnessBeaconV2,submitRoundALWAYS verifies (no phase flag, no admin off-switch); stores canonicalSHA-256(signature)randomness.
aere-contracts/test/AereRandomnessBeaconV2.test.js, real drand vectors.aerenew/contracts/hardhat.config.prague.js, pins the localhardhatnetwork to thepraguehardfork 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, randomnessb22aad47...440af3 - round 1000001: sig
a5bd91e5...c4218, randomness9f45f439...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:
-
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. -
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 == publishedb22aad47...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_G2andNEG_G2_GENERATORconstants 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.