How rng.dev Works
Overview
rng.dev generates verifiable random numbers by combining entropy from 8 independent blockchain sources. Every second, fresh data from fast-finality chains is mixed with stable base entropy from slower chains to produce cryptographically secure randomness.
The security guarantee: Compromising this beacon would require controlling multiple major blockchains simultaneously — an economic cost exceeding $80 billion.
The Generation Process
Every second, rng.dev:
- Snapshots blockchain data at the end of each round (T+1000ms)
- Combines block hashes and transaction IDs using SHA3-256 sequential mixing
- Derives a fair die value (1-6) using rejection sampling
- Publishes the result via API and WebSocket
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Aptos │ │ Arbitrum │ │ Base │ │ Bitcoin │
│ (~900ms) │ │ (~250ms) │ │ (~2s) │ │ (6 conf) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
└────────────────┴────────────────┴────────────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ ┌────────┴────────┐
│ Cardano │ │ Ethereum │ │ Solana │ │ Sui │
│ (finalized) │ │ (finalized) │ │ (~400ms) │ │ (~400ms) │
└──────┬──────┘ └──────┴──────┘ └──────┴──────┘ └────────┬────────┘
│ │ │ │
└────────────────┴────────────────┴───────────────────┘
▼
┌─────────────────┐
│ SHA3-256 │
│ Sequential │
│ Mixing │
└────────┬────────┘
▼
┌─────────────────┐
│ 256-bit Hash │
│ + Die Value │
└─────────────────┘
Blockchain Sources
We use 8 blockchains with different consensus mechanisms to ensure diversity:
| Chain | Finality | Block Time | Role |
|---|---|---|---|
| Aptos | ~900ms | ~160ms | Fresh each round |
| Arbitrum | ~250ms | ~250ms | Fresh each round (L2) |
| Base | ~2s | ~2s | Fresh each round (L2) |
| Bitcoin | ~60 min (6 conf) | ~10 min | Stable base entropy |
| Cardano | ~20 min | ~20 sec | Stable base entropy |
| Ethereum | ~15 min | ~12 sec | Stable base entropy |
| Solana | ~400ms | ~400ms | Fresh each round |
| Sui | ~400ms | ~380ms | Fresh each round |
Fast vs Slow Chains
- Fast-finality chains (Aptos, Arbitrum, Base, Solana, Sui) provide fresh entropy every second
- Slow-finality chains (Bitcoin, Cardano, Ethereum) provide stable base entropy that changes with each new block
This combination ensures:
- Fresh randomness every second from fast chains
- Deep economic security from proof-of-work (Bitcoin) and established proof-of-stake chains
- L2 chains (Arbitrum, Base) add diversity with different sequencer trust models
Why Multiple Sources?
Using multiple independent blockchains provides:
- Resilience — If one chain is unavailable, others continue
- Security — Manipulating multiple chains simultaneously is economically impractical
- Diversity — Different consensus mechanisms reduce correlated failures
What We Combine
Each round combines block hashes and transaction IDs from all 8 chains:
output_hash = SHA3(
aptos_block:aptos_hash:aptos_tx |
arbitrum_block:arbitrum_hash:arbitrum_tx |
base_block:base_hash:base_tx |
bitcoin_height:bitcoin_hash:bitcoin_tx |
cardano_slot:cardano_hash:cardano_tx |
ethereum_number:ethereum_hash:ethereum_tx |
solana_slot:solana_hash:solana_tx |
sui_checkpoint:sui_digest:sui_tx
)
Why Transaction IDs?
Block hashes alone could theoretically be influenced by block producers. Transaction IDs add entropy from a different trust source:
| Property | Block Hashes | Transaction IDs |
|---|---|---|
| Origin | Block producer | External users |
| Grinding | Producer can iterate nonce | Determined by TX content |
| Predictability | Producer knows first | Depends on mempool state |
Transaction IDs come from users submitting transactions, not from block producers. This provides entropy with a different trust assumption than block hashes alone.
Transaction Selection Rules
| Chain | Selection | Rationale |
|---|---|---|
| Aptos | 1st transaction (index 0) | Standard selection |
| Arbitrum | 1st transaction (index 0) | Standard selection (L2) |
| Base | 1st transaction (index 0) | Standard selection (L2) |
| Bitcoin | 2nd transaction (index 1) | Skip coinbase (miner-controlled) |
| Cardano | 1st transaction (index 0) | Standard selection |
| Ethereum | 1st transaction (index 0) | No coinbase equivalent |
| Solana | 1st transaction (index 0) | Standard selection |
| Sui | 1st transaction (index 0) | Standard selection |
Sequential Mixing Algorithm
We combine sources using SHA3-256 sequential mixing:
def combine_sources(sources: dict[str, str]) -> str:
"""
Combine multiple entropy sources into single hash.
Sources are processed in alphabetical order for determinism.
"""
state = ""
for name in sorted(sources.keys()): # Alphabetical order
if sources[name] is not None:
if state:
combined = f"{state}|{sources[name]}"
else:
combined = sources[name]
state = sha3_256(combined.encode()).hexdigest()
return state
Why Sequential Mixing?
- Order-dependent: Each hash depends on all previous inputs
- Deterministic: Same inputs always produce same output
- Verifiable: Anyone can recompute with the same inputs
Input Canonicalization
Each blockchain input follows a strict format:
| Chain | Format | Example |
|---|---|---|
| Aptos | {block_height}:{block_hash}:{txid} | 12345678:0xabc...:0xdef... |
| Arbitrum | {block_number}:{block_hash}:{txid} | 442950038:0x9a8...:0xa4b... |
| Base | {block_number}:{block_hash}:{txid} | 43507585:0x6f0...:0x63b... |
| Bitcoin | {block_height}:{block_hash}:{txid} | 831245:00000...3f:abc... |
| Cardano | {slot}:{block_hash}:{txid} | 123456789:abc...:def... |
| Ethereum | {block_number}:{block_hash}:{txid} | 19234567:0x8a3f...:0x... |
| Solana | {slot}:{blockhash}:{txid} | 245678901:5eykt...:3abc... |
| Sui | {checkpoint}:{digest}:{txid} | 12345678:abc...:def... |
Concatenation: Sources joined with pipe | separator in alphabetical order.
Die Value Derivation
The 256-bit hash is the primary output, but we also derive a die value (1-6) as a visual representation. The animated die on our homepage provides an intuitive display of ever-changing randomness.
We convert the hash to a fair die value using rejection sampling:
def derive_die_value(hash_hex: str) -> int:
"""
Convert hash to die value 1-6 using rejection sampling.
Avoids modulo bias by rejecting values >= 252.
"""
for i in range(0, len(hash_hex), 2):
byte_val = int(hash_hex[i:i+2], 16)
if byte_val < 252: # 252 = 6 * 42, evenly divisible
return (byte_val % 6) + 1
raise ValueError("Rejection sampling exhausted")
Why Rejection Sampling?
Simple modulo (hash % 6) creates bias because 256 is not evenly divisible by 6. Values 1-4 would appear slightly more often than 5-6.
Rejection sampling ensures perfect uniformity by only accepting values that map evenly to 1-6.
Partial Availability
If some sources are unavailable, we continue with available ones:
| Available | Status | Behavior |
|---|---|---|
| 8/8 | complete | Normal operation |
| 6-7/8 | partial | Generate with warning |
| 3-5/8 | degraded | Generate with prominent warning |
| 0-2/8 | failed | Do not generate |
The API response always indicates which sources were included.
Verification
Any round can be independently verified:
- Fetch the round data including all source inputs
- Recompute the hash using the same algorithm
- Compare with the stored hash
# Example verification
import hashlib
def verify_round(inputs: dict, expected_hash: str) -> bool:
state = ""
for name in sorted(inputs.keys()):
if inputs[name]:
combined = f"{state}|{inputs[name]}" if state else inputs[name]
state = hashlib.sha3_256(combined.encode()).hexdigest()
return state == expected_hash
See the Verification Guide for complete examples.
Statistical Analysis
We continuously run statistical tests to verify randomness quality:
Die value tests (details):
- Chi-squared test — Distribution uniformity
- Runs test — Sequence randomness
- Serial correlation — Independence between rounds
Beacon comparisons (details):
- K-S test — Distribution comparison against drand and NIST
- Chi-squared — Byte frequency comparison
- Runs test — Sequential pattern comparison
- Serial correlation — Dependency comparison
View live statistics on the homepage.
Next Steps
- Quick Start — Start using the API
- Verification Guide — Verify rounds yourself
- Threat Model — Understand the security model