Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

bls254 source pure

Package bls254 implements BLS signature verification over BN254, plus the hash-to-G1 construction and message encodin...

Overview

Package bls254 implements BLS signature verification over BN254, plus the hash-to-G1 construction and message encoding used by the SPLASH beacon.

Curve: alt_bn128 / BN254. G1: y^2 = x^3 + 3 over Fp. Points are encoded as 64 bytes (x|y), each coordinate 32-byte big-endian. G2 points (as used for the publisher's public key and the fixed generator) are 128 bytes, EIP-197 wire order: x_imag | x_real | y_imag | y_real.

There is no big-integer type in Gno, so field arithmetic goes through crypto/modexp (EIP-198 modular exponentiation) plus hand-rolled 4-limb compare/subtract/add for the one modular addition hash-to-G1 needs.

Constants 3

const DrandEvmnetChainHash, DrandEvmnetScheme, DrandEvmnetPeriodSeconds, DrandEvmnetGenesisTime, DrandEvmnetPubKeyHex

 1const (
 2	// DrandEvmnetChainHash identifies the chain. It is not used in verification
 3	// -- the signed message is keccak256(uint64 round) and nothing else -- so it
 4	// is here to name what a signature belongs to, and to make a wrong-chain
 5	// fixture obvious to a reader rather than only to the pairing check.
 6	DrandEvmnetChainHash = "04f1e9062b8a81f848fded9c12306733282b2727ecced50032187751166ec8c3"
 7
 8	// DrandEvmnetScheme is the scheme ID, and it is the reason drandcurve.gno
 9	// uses SVDW and not SSWU. See DrandDST.
10	DrandEvmnetScheme = "bls-bn254-unchained-on-g1"
11
12	// DrandEvmnetPeriodSeconds is the cadence: ONE ROUND EVERY THREE SECONDS.
13	//
14	// THIS NUMBER IS THE ONE THAT BREAKS THINGS. The SPLASH beacon it replaces
15	// ran at five minutes, so every round-denominated duration anywhere in this
16	// repository was 100x wrong the moment the key changed, while still being
17	// dimensionally consistent and therefore invisible to the build guards that
18	// compared rounds to rounds. Any realm expressing a DURATION in rounds must
19	// multiply by this constant and compare against seconds. See
20	// r/splash/beacon/DRAND-MIGRATION.md section 6.
21	DrandEvmnetPeriodSeconds = 3
22
23	// DrandEvmnetGenesisTime is round 1's unix timestamp, so round r is signed at
24	// DrandEvmnetGenesisTime + DrandEvmnetPeriodSeconds*(r-1).
25	//
26	// NO REALM ON THIS TOOLCHAIN CAN USE THIS FOR ANYTHING, and that is worth
27	// saying where someone will look for it: there is no block-timestamp getter,
28	// so a realm cannot compare a round to the wall clock. It is here so that a
29	// human or a test harness can, and so that the round <-> time mapping the
30	// whole pin argument rests on is written down next to the period.
31	DrandEvmnetGenesisTime = 1727521075
32
33	// DrandEvmnetPubKeyHex is the 128-byte G2 group public key in EIP-197 wire
34	// order (x_imag|x_real|y_imag|y_real) -- exactly the byte string /info serves
35	// as "public_key", which is why it needs no reordering here.
36	//
37	// It is the DKG's collective key, held by no single party. That is the point
38	// of the migration: under the SPLASH beacon one key holder knew every future
39	// round's randomness, and under this one nobody below the threshold does.
40	DrandEvmnetPubKeyHex = "07e1d1d335df83fa98462005690372c643340060d205306a9aa8106b6bd0b3820557ec32c2ad488e4d4f6008f89a346f18492092ccc0d594610de2732c8b808f0095685ae3a85ba243747b1b2f426049010f6b73a0cf1d389351d5aaaa1047f6297d3a4f9749b33eb2d904c9d9ebf17224150ddd7abd7567a9bec6c74480ee0b"
41)
source

The drand "evmnet" chain's identity: the four facts about the beacon that are properties of the CHAIN rather than of any code here.

THIS IS THE ONE PLACE THE GROUP PUBLIC KEY IS WRITTEN DOWN. Before this file it appeared only in test files, duplicated across drandvectors_test.gno and drandgas_test.gno; both now read it from here, and r/splash/beacon decodes it once at init. A second copy of a verification key is a second thing to get wrong, and the wrong one would reject every real signature or -- far worse in a different scheme -- accept a forged one.

IT LIVES IN THIS PURE PACKAGE, NOT IN THE REALM, because it describes drand, not SPLASH. The realm that consumes it is one consumer; the tests are another; r/splash/arcade/raffle needs PeriodSeconds for a build-time guard and must not have to import a realm to get it.

PROVENANCE, AND IT IS NOT DOCUMENTATION. Every value below was read from drand's own /info endpoint and is byte-identical from three independent relays -- api.drand.sh, api2.drand.sh and drand.cloudflare.com. The verbatim curl commands and responses are recorded twice, in testdata/drand/rounds.raw.txt here and in r/splash/beacon/testdata/drand-rounds.raw.txt.

const DOMAIN

1const DOMAIN = "SPLASH-BEACON-BN254-G1-v1"
source

DOMAIN is the hash-to-G1 domain separation tag for the SPLASH beacon.

const DrandDST

1const DrandDST = "BLS_SIG_BN254G1_XMD:KECCAK-256_SVDW_RO_NUL_"
source

DrandDST is the hash-to-curve domain separation tag for drand's bls-bn254-unchained-on-g1 scheme (the "evmnet" beacon, chain hash 04f1e9062b8a81f848fded9c12306733282b2727ecced50032187751166ec8c3).

Functions 7

func DrandRoundMessage

1func DrandRoundMessage(round int64) []byte
source

DrandRoundMessage returns the message drand's bls-bn254-unchained-on-g1 scheme signs for a round: keccak256 of the round number as a big-endian uint64. That is drand v2.1.7's DigestBeacon for this scheme, verbatim.

The chain is *unchained*: the previous round's signature is not part of the message, so any round can be verified on its own.

Returns nil for a negative round. drand round numbers are uint64 and start at 1; a negative value is a caller bug, and silently reinterpreting it as a huge unsigned round would produce a message for a round nobody will ever sign. Round 0 is encoded normally -- it is a well-defined byte string -- but no signature exists for it, so verification will simply fail.

func HashToG1

1func HashToG1(msg []byte) []byte
source

HashToG1 maps msg to a G1 point via try-and-increment. Returns 64 bytes (x|y). Panics if the loop exhausts its 64-attempt bound.

func HashToG1Drand

1func HashToG1Drand(msg []byte) []byte
source

HashToG1Drand maps msg to a G1 point the way drand's bls-bn254-unchained-on-g1 scheme does: RFC 9380 hash_to_curve, SVDW map, expand_message_xmd with H = legacy Keccak-256 and DST = BLS_SIG_BN254G1_XMD:KECCAK-256_SVDW_RO_NUL_.

Returns 64 bytes, x|y. Returns nil if anything in the construction fails, which for a fixed DST means only that the native G1 addition rejected its input -- it never should.

This is NOT HashToG1. HashToG1 is the existing SPLASH beacon's try-and-increment construction over a different domain tag, and the two return different points for the same message. Callers must not mix them.

func NegateG1

1func NegateG1(pt []byte) []byte
source

NegateG1 returns (x, P-y) for a 64-byte G1 point. Returns nil on bad input.

func RoundMessage

1func RoundMessage(chainID string, round int64) []byte
source

RoundMessage builds the canonical message for a beacon round: chainID || "|" || decimal(round).

func Verify

1func Verify(pubkey, msg, sig []byte) bool
source

Verify reports whether sig is the BLS signature over msg under pubkey. pubkey is 128 bytes (G2, imaginary-first), sig is 64 bytes (G1). Verify is total: wrong lengths, unreduced coordinates, off-curve points and an invalid precompile result all yield false. It never panics.

func VerifyDrand

1func VerifyDrand(pubKey []byte, round int64, sig []byte) bool
source

VerifyDrand reports whether sig is the drand beacon signature for round under pubKey, for the bls-bn254-unchained-on-g1 scheme (drand's "evmnet" chain, hash 04f1e9062b8a81f848fded9c12306733282b2727ecced50032187751166ec8c3).

pubKey is the 128-byte G2 group public key in EIP-197 wire order (x_imag|x_real|y_imag|y_real), which is exactly the byte string the drand /info endpoint serves as "public_key". sig is the 64-byte G1 signature from /public/<round>. The chain is unchained, so no previous signature is needed.

The randomness for a verified round is sha256(sig) -- the same derivation r/splash/beacon already uses. This function deliberately does not compute it: verifying and deriving are separate steps and a caller that conflates them tends to derive from an unverified signature.

VerifyDrand is total. It never panics, and returns false for every malformed input: wrong pubKey or sig length, a negative round, a signature coordinate at or above p, an off-curve point, and an invalid precompile result.

This is NOT Verify. Verify implements the SPLASH beacon's own scheme (chainID || "|" || decimal(round), SHA-256, try-and-increment hash-to-G1) and will reject every real drand signature; VerifyDrand will reject every SPLASH one. Pick by which beacon produced the signature, never by which is to hand.

Dependencies

Source Files 7