Using Public Randomness from drand
In this blog post, we’ll dive into the fascinating world of public randomness and provide a step-by-step tutorial on how to consume randomness from drand and use it in your applications. Get ready for an exciting journey into the heart of randomness!
Why Public Randomness?
Most developers are familiar with private randomness: we use randomness to generate keypairs, randomize back off timings in distributed systems, choose stats for games, and a plethora of other things. This makes sense—if the randomness used to generate our keypairs was public, bad actors could intercept all our communications or steal our bitcoin!
However, some applications that currently use private randomness might be better served using public randomness. For example, online casinos generate random numbers to order cards dealt or determine a winner. We must trust that these casinos use effective random number generators, but history has shown this is not always the case. If they committed to a random number generated by a third party (or third parties), or used some kind of verifiable randomness, this could improve fairness and increase user trust.
For cases such as random distribution of welfare (e.g., universal basic income experiments), the government could use a third party such as an NGO to draw random numbers. However, this still constitutes a weak link, as a single third party could be unduly influenced to bias the randomness.
Enter drand
drand is built upon a threshold network. Instead of relying on a single third party, multiple third parties work together to generate a random number. It exploits the fact that a hashed signature has all the properties of randomness as long as nobody holds the secret key. Due to the way drand distributes keys, no party ever has the whole secret key, and as such no one can ever predict the signature generated—woohoo, randomness! These properties are why projects such as Social Income are building on top of drand for their randomness!
Using drand
Presently, the drand network generates a random hex string every thirty seconds. It can be verified using the public key of the network, so you can receive randomness from an untrusted party and verify that it’s truly been created by the drand network. This powerful property simplifies your trust assumptions: you can receive randomness without having to trust the node that sent you the message because you can verify it for yourself.
In this tutorial, we're going to fetch randomness using the official JavaScript client, but you could also use the official Go client, one of the multiple unofficial Rust clients, or fetch it via curl/libp2p/other channels and verify the BLS signatures yourself.
Setting Up
First, make sure you have a relatively recent version of Node.js (17+) and npm installed. Instructions can be found here.
Then, create a new folder for your project:
mkdir drand-client-tutorial
cd drand-client-tutorial
npm init
npm install drand-client
As the drand client is bundled as an ESM module, add "type": "module"
in your package.json
to use it.
Create a new file at the root of the project called index.js
and open it in your editor of choice.
Fetching Randomness
The most basic primitive is a Chain
—it represents a single network instance hosted by a node. Networks can have different parameters such as frequency, algorithm, and participants, but for our purposes, the default network is enough.
We're going to connect to the HTTP API of the drand team’s nodes (api.drand.sh), but you could also use Cloudflare's API endpoint (drand.cloudflare.com), or StorSwift’s relay (api.drand.secureweb3.com:6875).
import { HttpChain } from "drand-client";
const chain = new HttpChain("<https://api.drand.sh>");
chain.info().then(info => console.log(info));
Run this with:
node --experimental-fetch index.js
(Node versions greater than 18 can skip the --experimental-fetch
flag!)
Fetching the Latest Beacon
To fetch the latest beacon, use the fetchBeacon
function:
import { HttpChain, fetchBeacon } from "drand-client";
const chain = new HttpChain("<https://api.drand.sh>");
fetchBeacon(chain, 1).then(beacon => console.log(beacon));
To get the latest beacon by time:
import { HttpChain, HttpChainClient, fetchBeaconByTime } from "drand-client";
const chain = new HttpChain("<https://api.drand.sh>");
const client = new HttpChainClient(chain);
fetchBeaconByTime(client, Date.now()).then(beacon => console.log(beacon));
For real-time applications, use the watch
function to listen for new beacons:
import { HttpChain, HttpChainClient, watch } from "drand-client";
const chain = new HttpChain("<https://api.drand.sh>");
const client = new HttpChainClient(chain);
const beacons = watch(client, new AbortController());
for await (const beacon of beacons) {
console.log(beacon);
}
Using Randomness Effectively
If we only ever needed a 64-byte random value in our applications, we'd be finished now. Unfortunately, real-life applications often require more tailored randomness. Here’s how to handle different use cases:
Coin Flip Example
import { HttpChain, fetchBeaconByTime } from "drand-client";
const chain = new HttpChain("<https://api.drand.sh>");
fetchBeaconByTime(chain, Date.now()).then(beacon => {
const flip = BigInt("0x" + beacon.randomness) % BigInt(2);
if (flip === BigInt(0)) {
console.log(beacon.round + ": HEADS!");
} else {
console.log(beacon.round + ": TAILS!");
}
});
Avoiding Bias in Random Selection
For selecting a random winner from a lottery:
import { HttpChain, fetchBeaconByTime } from "drand-client";
const participants = ["alice", "bob", "carol", "dave", "edward", "fiona", "georgina"];
const chain = new HttpChain("<https://api.drand.sh>");
fetchBeaconByTime(chain, Date.now()).then(beacon => {
const randomNumber = BigInt("0x" + beacon.randomness);
let winnerIndex = randomNumber % BigInt(participants.length);
// Rejection sampling to avoid bias
while (winnerIndex >= BigInt(participants.length)) {
winnerIndex = BigInt("0x" + crypto.randomBytes(2).toString('hex')) % BigInt(participants.length);
}
console.log(`The winner is ${participants[winnerIndex]}`);
});
Wrapping Up
Using drand can bring a new level of fairness and verifiability to your applications. Whether you’re flipping coins or selecting lottery winners, drand's verifiable randomness ensures transparency and trust. We love hearing about the projects you’re working on, so feel free to join us on Slack and share your stories or ask for help.
Suggested Readings:
- Random Number
- Threshold Cryptosystem
- Pseudorandom Number Generator
- Universal Basic Income
- Shamir's Secret Sharing
- Timing Attack
- Dual_EC_DRBG
Until next time, happy coding! 🚀