// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import {BLS2} from "./vendor/bls-solidity/BLS2.sol"; /** * Stateless verifier for drand quicknet (`bls-unchained-g1-rfc9380`). * * It accepts only the canonical 48-byte compressed signature returned by the * drand HTTP API. The returned randomness is drand's canonical * `sha256(signature)` value, so the contract and public verifier derive exactly * the same draw input. * * The vendored BLS primitives are experimental and unaudited. This contract is * suitable for the controlled live rehearsal only until independently audited. */ contract GMDrandQuicknetVerifier { string public constant DST = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_"; bytes32 public constant CHAIN_HASH = 0x52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971; uint64 public constant PERIOD_SECONDS = 3; uint64 public constant GENESIS_TIMESTAMP = 1_692_803_367; uint256 public constant COMPRESSED_SIGNATURE_LENGTH = 48; error BeforeGenesis(); error InvalidSignatureLength(); error InvalidBeacon(); function currentRound() public view returns (uint64) { if (block.timestamp < GENESIS_TIMESTAMP) revert BeforeGenesis(); return uint64((block.timestamp - GENESIS_TIMESTAMP) / PERIOD_SECONDS) + 1; } function verifyBeacon(uint64 round, bytes calldata signature) external view returns (bytes32 randomness) { if (signature.length != COMPRESSED_SIGNATURE_LENGTH) revert InvalidSignatureLength(); BLS2.PointG1 memory signaturePoint = BLS2.g1UnmarshalCompressed(signature); BLS2.PointG1 memory messagePoint = BLS2.hashToPoint(bytes(DST), abi.encodePacked(sha256(abi.encodePacked(round)))); (bool pairingSuccess, bool callSuccess) = BLS2.verifySingle(signaturePoint, _publicKey(), messagePoint); if (!pairingSuccess || !callSuccess) revert InvalidBeacon(); return sha256(signature); } function _publicKey() private pure returns (BLS2.PointG2 memory) { // Uncompressed quicknet public key from Randamu's MIT QuicknetRegistry, // corresponding to the official drand quicknet chain hash above. return BLS2.PointG2( 0x03cf0f2896adee7eb8b5f01fcad39122, 0x12c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d106451, 0x0d1fec758c921cc22b0e17e63aaf4bcb, 0x5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a, 0x01a714f2edb74119a2f2b0d5a7c75ba9, 0x02d163700a61bc224ededd8e63aef7be1aaf8e93d7a9718b047ccddb3eb5d68b, 0x0e5db2b6bfbb01c867749cadffca88b3, 0x6c24f3012ba09fc4d3022c5c37dce0f977d3adb5d183c7477c442b1f04515273 ); } }