/

Architecture

DICE has three moving pieces: a fleet of ESP32-S3 devices that produce entropy, an off-chain coordinator that shuttles messages, and a Solana program that settles each round atomically.

Three layers

Every round touches all three layers, in order: hardware generates entropy, protocol aggregates it safely, program verifies and finalises it. You only interact with layer three — but knowing what lives where explains why the guarantees work.

1

Hardware

ESP32-S3 fleet

RNG sampling, ECDSA signing, CBOR over USB serial.

2

Coordinator

Rust daemon

Watches chain, requests commits, collects reveals, posts TXs.

3

Program

Solana · Anchor

Verifies signatures, aggregates, pays operators, CPIs into you.

The hardware layer

Every DICE node is an ESP32-S3 running our firmware. The chip has a hardware true-RNG peripheral that samples from analog noise in the radio front-end. It also has a small persistent flash region that stores one thing: the device's ECDSA private key, written at provisioning and never exported.

What a node exposes

  • Device ID — deterministic: SHA-256(device_pubkey). The program verifies this on every reveal so a device can't pretend to be another.
  • Round API — over USB serial, CBOR-framed. The coordinator sends a round nonce; the device returns commit, then later reveal + signature.
  • Signed payloads — every message crossing the USB boundary is signed. The private key never leaves the chip.

Why ESP32-S3 specifically

Low unit cost, deterministic behaviour, a RTC that survives power cycles, and a hardware RNG that independent audits have blessed for cryptographic use. We use a subset of the chip — no Wi-Fi, no Bluetooth — so the attack surface is small.

The commit-reveal protocol

A naive oracle picks entropy after seeing the request, which lets it bias the output. DICE picks entropy before anyone sees it, using a two-phase protocol that's been well-understood since Rabin.

Phase 1 · Commit

Each selected node generates a local random entropy_i ∈ {0,1}^256 plus a round nonce, then computes commit_i = SHA-256(entropy_i ‖ nonce). The coordinator posts all commit_i values to chain in a single submit_round transaction. At this point the entropy is locked — any change would break the pre-image.

Phase 2 · Reveal

After commits land, each node releases reveal_i = entropy_i along with its ECDSA signature over the reveal. The coordinator submits all reveals in one submit_reveal transaction. The program verifies that SHA-256(reveal_i ‖ nonce) == commit_i and that the signature matches the device's registered pubkey.

Phase 3 · Aggregate

The final randomness is randomness = SHA-256(reveal_1 ‖ reveal_2 ‖ … ‖ reveal_n). One honest reveal makes the output uncontrollable by everyone else. The program stores this 32-byte value on the channel and CPIs your callback with it.

Why SHA-256, not XOR?

XOR is algebraically fragile: a last-mover with knowledge of all other shares can grind to a target output. SHA-256 breaks that — the adversary would need to find pre-images.

The on-chain program

The DICE program (FMwPuCjkfZXN2MuNJQiUzZC3hnxHcD8mrTuntsqA84XD) is an Anchor program with four families of instructions:

  • Registration — device enrolment, channel creation, coordinator binding.
  • Roundsrequest_randomness_auto, submit_round, submit_reveal, finalize.
  • Callbackdeliver_callback CPIs into your program with the DiceResult payload.
  • Payoutsclaim_rewards_v2 credits node vaults from the round escrow; 70/20/10 split.

Every state transition is an on-chain event, so a full history of commits, reveals, and signatures is publicly reproducible. See Verifying randomness for the recipe.

A round, end to end

Here's the wall-clock view — numbers taken from a 50-round devnet bench on real hardware, v7.7 streaming:

tLayerEvent
0 msYour programrequest_randomness_auto lands. Round is open.
~400 msCoordinator → devices4 nodes selected, commit request broadcast.
~1.5 sChainsubmit_round lands with all 4 commits.
~3.0 sChainsubmit_reveal lands with signatures.
~4.0 sChaindeliver_callback CPIs your dice_callback. Round finalized. Operators paid.

From the caller's perspective: submit TX, wait 4 seconds, read your settled game state. Streaming mode: avg 4.05 s, p50 3.99 s, p95 4.62 s over 50 rounds, 98% pass rate.