beacon
The x/beacon module is daqq’s randomness beacon. It produces a single shared 256-bit seed per round that every node in the network agrees on.
Round structure
A round lasts 50 blocks (RoundDuration = 50) and is divided into three phases:
| Phase | Block range within round | What participants do |
|---|---|---|
| Commit | 0–30 | Submit MsgCommit{roundID, hash} where hash = sha256(secret) |
| Reveal | 31–45 | Submit MsgReveal{roundID, secret}; chain verifies sha256(secret) == commits[creator] |
| Finalise | 50 (EndBlocker) | Chain XORs all reveals, hashes the result, stores Seeds[roundID] |
gantt
title One round (50 blocks)
dateFormat X
axisFormat %s
section Phase
Commit (0-30) :a, 0, 31
Reveal (31-45) :b, 31, 15
Idle (46-49) :c, 46, 4
Finalise @ block 50 :crit, d, 50, 1
Protocol
1. Commit
Each participant generates a secret s_i ∈ {0,1}^256 and submits H_i = sha256(s_i).
Implemented in quantum-chain/x/beacon/keeper/msg_server_commit.go:13-50. Commits outside the commit window are rejected.
2. Reveal
The same participant later sends s_i. The chain checks:
A reveal whose hash does not match the stored commit is rejected, so participants are bound to the secret they committed.
Implemented in quantum-chain/x/beacon/keeper/msg_server_reveal.go:16-64.
3. Aggregate (EndBlocker at height % 50 == 0)
Let be the set of revealed secrets for the round. The final seed is
XOR is commutative, so the order in which reveals are read does not affect the result. Every node computes the same value bit-for-bit.
Implemented in quantum-chain/x/beacon/keeper/abci.go:48-65.
sequenceDiagram
autonumber
participant Alice
participant Bob
participant Carol
participant Chain
Note over Alice,Carol: Block H, round R, commit phase
Alice->>Chain: MsgCommit{R, H_A = sha256(s_A)}
Bob->>Chain: MsgCommit{R, H_B = sha256(s_B)}
Carol->>Chain: MsgCommit{R, H_C = sha256(s_C)}
Note over Alice,Carol: Block H+31, round R, reveal phase
Alice->>Chain: MsgReveal{R, s_A}
Bob->>Chain: MsgReveal{R, s_B}
Carol->>Chain: MsgReveal{R, s_C}
Chain->>Chain: verify sha256(s_x) == H_x
Note over Chain: Block H+50, EndBlocker
Chain->>Chain: combined = s_A XOR s_B XOR s_C
Chain->>Chain: seed = sha256(combined)
Chain->>Chain: Seeds[R] = seed
Chain-->>Alice: event NewRound{R, seed}
Chain-->>Bob: event NewRound{R, seed}
Chain-->>Carol: event NewRound{R, seed}
State
From quantum-chain/x/beacon/keeper/keeper.go:26-29:
| Collection | Key | Value | Meaning |
|---|---|---|---|
RoundInfo | – | uint64 | Current round counter |
Commits | (roundID, creator) | hash | Commit hash per participant per round |
Reveals | (roundID, creator) | secret | Revealed secret per participant per round |
Seeds | roundID | finalSeed | Final seed for a completed round |
Events
From quantum-chain/x/beacon/types/events.go:4-8:
EventType: new_round
Attributes:
round_id: <uint64>
seed: <hex string>Subscribe to tm.event='Tx' AND new_round.round_id EXISTS to receive shared-seed notifications.
Security model
| Assumption | Consequence |
|---|---|
| At least one honest reveal with high-entropy secret | Seed is unpredictable to adversaries before reveal phase ends |
| Adversary controls all reveals | Adversary can choose seed by withholding their reveal until last |
| Adversary withholds a reveal | That participant’s secret is excluded; honest XOR still produces an unpredictable value |
Integration
x/qcledger consumes the seed via the BeaconKeeper interface:
// quantum-chain/x/qcledger/types/expected_keepers.go:10-13
type BeaconKeeper interface {
GetSeed(ctx context.Context, roundID uint64) (string, error)
}MsgSubmitResult (in qcledger) calls GetSeed(roundID) and refuses to accept results until the beacon has finalised the round.