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

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