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.

beacon/v2 package

Overview

Package beacon is SPLASH's on-chain record of the drand evmnet randomness beacon.

WHAT CHANGED, AND WHY IT IS NOT A REFACTOR (2026-09-28). This realm used to verify signatures from a beacon SPLASH itself ran: one compiled-in BLS key, held by this project, publishing a round every five minutes. The key holder could sign ANY round at will and therefore knew every future round's randomness permanently, which is r/splash/arcade/raffle/AUDIT-2026-09-28.md findings 4 and 10 and was the single worst property of the product. It now verifies drand evmnet, a threshold beacon this project does not control. Two consequences run through every comment below:

  • NOBODY CAN SIGN A FUTURE ROUND. Round r's signature does not exist before its time, and no party below the DKG threshold can make it exist. That is what the games' round bindings are for and it is now true rather than assumed.
  • NOBODY CAN DESTROY A PAST ROUND. Every round from 1 to the head is served forever by at least three independent relays, and Publish is permissionless, so anything deleted here is re-creatable by anyone. "The randomness is lost" stopped being a reachable state, which is why Prune, PruneWindow and the raffle's resolvable() are gone -- see DRAND-MIGRATION.md section 3.

THIS REALM IS NOT A MIRROR OF DRAND AND MUST NEVER BECOME ONE. At a three-second cadence, keeping up would mean one Publish every three seconds at 16,970,375 gas each: 0.62% of EVERY BLOCK on the chain, for ever, for data that is read about fifty times a year (DRAND-MIGRATION.md sections 2 and 9.5 have the measured arithmetic; 9.5 also records that this said 2.4% until 2026-09-30, off by the same 4.4x as every other gas figure derived from the pre-optimisation VerifyDrand, and that at 0.62% the STORAGE cost rather than the gas share is what rules mirroring out). It is an ON-DEMAND RECORD: a round is stored when something on this chain has a reason to look at it, and nothing else is stored at all.

There is no owner, no pause and no upgrade path. Once a round is published its randomness is fixed for ever.

Function

EstimatedLatestRound

func EstimatedLatestRound() int64
Ask AI Claude ChatGPT

EstimatedLatestRound is this realm's best estimate of the round drand is currently on. Panics if no round has ever been published.

A NEW CALLER SHOULD REACH FOR CurrentRound INSTEAD, AND THIS FUNCTION IS KEPT ONLY FOR WHAT ALREADY CONSUMES IT. This estimate extrapolates from the last RELAYED round, and this realm's own TestKnownWeaknessRelayingTheRoundJustAboveTheEstimateBuysTheWholeStalenessBudgetBack proves that extrapolation is poisonable for the cost of one Publish of a freely-fetched PAST round: relaying est+1 resets the staleness clock to zero while moving the estimate forward by exactly one round. CurrentRound instead reads the block's own clock and computes drand's round by arithmetic, so there is no relayed round to poison. See CurrentRound's comment for the full account, including what trusting the block's clock costs in its place. Nothing that calls EstimatedLatestRound today has been switched over -- that is a separate change -- so this function must keep working exactly as it does.

IT IS AN ESTIMATE AND THE NAME SAYS SO. There is no way to know the true current round on-chain: proving "round r is not signed yet" needs an UPPER bound on elapsed real time, published rounds give only a LOWER bound, and chain height gives none at all because a halted chain advances no height while drand advances 28800 rounds a day. Every round-number pin in this system is therefore an assumption about this chain keeping pace with drand, and this function is where that assumption lives instead of being spread across three realms.

THE CONSTRUCTION: take anchorScaled — the highest round-versus-height intercept any relay has ever evidenced — and read this height off that line. It is exactly latestRound at the moment latestRound is relayed. READ anchorScaled BEFORE CHANGING THIS: extrapolating from the highest round's own publication height instead is the obvious simplification, and it is exploitable.

WHY IT IS STRICTLY BETTER THAN Latest() FOR A PIN. A consumer pinning Latest() + offset measures its offset from whenever somebody last happened to relay something, so the pin drifts backwards in real time as the beacon goes quiet — and the raffle's offset is exactly one day larger than a week, so a single day of staleness puts the pinned round's randomness on the public API BEFORE the week's ticket sales close, which is finding 10's last-buyer attack verbatim. The correction removes that failure entirely in the honest case.

THE ERROR IS BOUNDED BY REFUSAL, AND THAT IS NEW AS OF THE STALENESS BOUND. The conversion assumes A = 86400/blocksPerDay = 3.29998 s blocks, so its error is S*(A - B) seconds of drand time for S blocks since the anchor was last raised and a real average block time B — 2.4 hours at one day of S and a 10% drift in B, NOT the ~14 minutes this comment claimed until the figure was re-derived (that number divided by the period twice; see maxEstimateStaleBlocks and AUDIT-2026-09-28.md finding 13). Unbounded S makes it unbounded in BOTH directions, so this function now REFUSES above maxEstimateStaleBlocks rather than returning a number nobody can put a bound on. What the refusal costs, and why it is nevertheless right, is at maxEstimateStaleBlocks and in AUDIT-2026-09-28.md finding 12.

MONOTONE IN HEIGHT AND NEVER BELOW latestRound: ChainHeight() only rises and anchorHeight/anchorScaled never move except upward, so the correction term is never negative.

Command

gnokey query vm/qeval -remote "https://rpc.onyx.testnets.gno.land" -data "gno.land/r/g1r4aw43kagaaquhegxw6xr346h9f2qu2ezf0jar/beacon/v2.EstimatedLatestRound()"

Result