// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IFutureRandomness} from "./IFutureRandomness.sol"; import {BLS} from "./vendor/BLS.sol"; /// @notice Verifies the public drand evmnet beacon using its immutable mainnet key. /// @dev The vendored cryptographic library is experimental and unaudited. /// Consumers must fix all outcome inputs before the target beacon is available. contract DrandEvmnetVerifier is IFutureRandomness { bytes32 public constant CHAIN_HASH = 0x04f1e9062b8a81f848fded9c12306733282b2727ecced50032187751166ec8c3; uint64 public constant GENESIS_TIME = 1727521075; uint64 public constant PERIOD = 3; string public constant SCHEME_ID = "bls-bn254-unchained-on-g1"; string public constant DST = "BLS_SIG_BN254G1_XMD:KECCAK-256_SVDW_RO_NUL_"; struct Beacon { bytes32 value; bool available; } mapping(uint64 => Beacon) private beacons; error InvalidRound(); error FutureBeacon(); error InvalidSignature(); event BeaconVerified(uint64 indexed beaconRound, bytes32 randomness, bytes signature); function genesisTime() external pure returns (uint64) { return GENESIS_TIME; } function period() external pure returns (uint64) { return PERIOD; } function getRandomness(uint64 beaconRound) external view returns (bytes32 value, bool available) { Beacon storage beacon = beacons[beaconRound]; return (beacon.value, beacon.available); } function beaconTimestamp(uint64 beaconRound) public pure returns (uint256) { if (beaconRound == 0) revert InvalidRound(); return uint256(GENESIS_TIME) + (uint256(beaconRound) - 1) * PERIOD; } /// @notice A read-only, on-chain cryptographic check of a 64-byte G1 signature. /// @dev The exact uint64 round encoding and domain separator are fixed by drand. function verifyBeacon(uint64 beaconRound, bytes memory signature) public view returns (bytes32) { if (beaconRound == 0) revert InvalidRound(); if (signature.length != 64) revert InvalidSignature(); BLS.PointG1 memory signaturePoint = BLS.g1Unmarshal(signature); if (!BLS.isValidPointG1(signaturePoint)) revert InvalidSignature(); (bool pairingSuccess, bool callSuccess) = BLS.verifySingle( signaturePoint, publicKey(), BLS.hashToPoint(bytes(DST), abi.encodePacked(keccak256(abi.encodePacked(beaconRound)))) ); if (!pairingSuccess || !callSuccess) revert InvalidSignature(); return sha256(signature); } /// @notice Anyone can relay a public proof; the relayer cannot choose its value. function submitBeacon(uint64 beaconRound, bytes calldata signature) external returns (bytes32 value) { if (block.timestamp < beaconTimestamp(beaconRound)) revert FutureBeacon(); value = verifyBeacon(beaconRound, signature); if (!beacons[beaconRound].available) { beacons[beaconRound] = Beacon(value, true); emit BeaconVerified(beaconRound, value, signature); } } /// @dev Coordinates match the immutable mainnet evmnet key and upstream demo. function publicKey() public pure returns (BLS.PointG2 memory) { return BLS.PointG2( [ uint256(0x557ec32c2ad488e4d4f6008f89a346f18492092ccc0d594610de2732c8b808f), uint256(0x7e1d1d335df83fa98462005690372c643340060d205306a9aa8106b6bd0b382) ], [ uint256(0x297d3a4f9749b33eb2d904c9d9ebf17224150ddd7abd7567a9bec6c74480ee0b), uint256(0x95685ae3a85ba243747b1b2f426049010f6b73a0cf1d389351d5aaaa1047f6) ] ); } }