Freedom, dignity, and justice for Palestinians.
#
#
#
#
#
#
#

The balls will be updated automatically each 30 seconds.

How it Works?

Structure

Drand Logo

Drand Network

Quoting drand.love: “drand (lower case, pronounced "DEE-rand") is a distributed randomness beacon daemon written in the Go programming language. It generates collective, publicly verifiable, unbiased, and unpredictable random values at fixed intervals using advanced cryptographic techniques.”

League of Entropy Logo

League of Entropy

The League of Entropy is a voluntary consortium, that runs drand, to provide publicly verifiable, unbiased, and unpredictable randomness. The consortium consits of big companys like Cloudflare, Ethereum Foundation, and more where they use drand to publish randomness.

Drand Network Modes

Network in Chain Mode

In chain mode, each round depends on the previous round, which mean that you can't predict future rounds' message.

Drand default chain mode Drand default chain mode

Network in Unchained Mode

In unchained mode, each round does not depend on the previous round, which mean that you can predict future rounds' message.

Drand default chain mode Drand default chain mode

The League of Entropy's Networks

There are three networks that we can access their randomness from the League of Entropy's endpoints:

  1. default network, which runs in chained mode and interests us for the LOTO system.
  2. quicknet network, which runs in unchained mode and help us to create time-lock encrypted messages.
  3. evmnet network, which runs in unchained mode for code that runs in the EVM.

The default Network

Each network has a chain hash that acts as an identifier for the network. The chain hash for the default network is 8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce.

We can query the network by performing an HTTP request, either by the network's id or the network's chain hash, to the API-V2 endpoints. An example curl query:

curl -L 'https://api.drand.sh/v2/beacons/default/rounds/latest' -H 'Accept: application/json'

The returned json will look like this:

{
  "round": 6522645,
  "signature": "b6b13209bfd5377974588d0666a5a9363e5f017764fa379cdb688bd3250b633941f245c8220d3e4890cd207d8694f174011831566b0cf2389189c6cd6961deb54178dfdf9b9ae59f03ed1027d1d60bf1ba331ea077cd5da798154b5d3b619062",
  "previous_signature": "97660b3fee2af886f96a0b862cee6de9747f9abaea9632c349f0f3331d7581724d3c43a134a74497b89c0ee533994c340412b1188c5d6945ce86dd1c6a20803502699ff3ea5b10fa9e4a21442ed5b6b4c4f5d90e96a0fead44193929dcfd95b4"
}

But where's the randomness? The randomnes is the signature where you can run it through a KDF, like a hash function. In API-V1, they add randomness in the returned JSON, which is just a sha256 digest of the signature.

Using API-V2 endpoints, we will decrease the amount of transefered data and derive the randomness using any KDF. To be compatible with API-V1, which provides randomness in the returned JSON, I used sha256 to derive the randomness when querying API-V2.

default is a chained network, so the result will contain round, signature, and previous_signature, to be able to verify the randomness.

We can't just pass the signature as it to sha256, we need first to decode it, then pass it to sha256, because API-V1 run sha256 on the raw value of the signature. In go, it will look like this:

decodedSignature, err := hex.DecodeString(signature)
if err != nil {
        // ...
}

kdf := sha256.New()
if _, err := kdf.Write(decodedSignature); err != nil {
        // ...
}

randomness := hex.EncodeToString(kdf.Sum(nil))

You can replace sha256 with any cryptographically secure KDF of your choice.

How the Balls Get Drawn?

After having the sha256 (hex) digest, we can go throught it one byte at a time and convert it to a range of 1 to 42 until we have 7 unique balls. A byte consists of 2 hex digits, and the range goes from 0 to 255 covering 256 numbers.

The question is: “How can we convert that single-byte randomness to a range of 1 to 42?”

Speaking programmatically, can't we just do mod 42 and call it a day? No. Why?

Because 42 doesn't divide 256 (256/42=6.09), so there's a small bias in the first 4 numbers (0, 1, 2, 3), that are more frequent to be drawn. Here's the frequency:

Distribution of x mod 42 Distribution of x mod 42

What is the Solution? We can simply discard the numbers greater than or equal to 252 (255-252+1=4), because they cause it. The frequency is now:

Distribution of x mod 42 Distribution of x mod 42

Now we can build the LOTO system!

Building the LOTO System

For this system, I wanted:

  • No backend.
  • Everyone accessing the page will have the same 7 balls, and keep periodically updating upon new rounds.
  • Each drawn ball share the same color for everyone.

I've build the website using HTML, CSS, JS, and WebAssembly (Go), elliminating the need for a centralized server. The second requirement is solved by calling each 01 and 31 second of each minute an exported Go function that fetchs and updates the balls. Why each 01 and 31 of each minute? Because, the default network updates the randomness each 30 seconds, so on 00 and 30 of each minute, and by using 01 and31, I am letting the network propagates the update.

To accomplish the third requirement, I used the chosen random byte as a source of entropy when choosing the color of the ball, and the random byte is the same accross all everyone for round n for each ball. The go code looks like this:

func randomBallColorFromSeed(randomness byte) BallColor {
        source := rand.NewSource(int64(randomness))
        r := rand.New(source)
        return ballColors[r.Intn(len(ballColors))]
}

LOTO System in Practice

Current randomness:

Byte 1: 1 →

Byte 2: 2 →

Byte 3: 3 →

Byte 4: 4 →

Byte 5: 5 →

Byte 6: 6 →

Byte 7: 7 →

Byte 8: 8 →

Byte 9: 9 →

Byte 10: 10 →

Byte 11: 11 →

Byte 12: 12 →

Byte 13: 13 →

Byte 14: 14 →

Byte 15: 15 →

Byte 16: 16 →

Byte 17: 17 →

Byte 18: 18 →

Byte 19: 19 →

Byte 20: 20 →

Byte 21: 21 →

Byte 22: 22 →

Byte 23: 23 →

Byte 24: 24 →

Byte 25: 25 →

Byte 26: 26 →

Byte 27: 27 →

Byte 28: 28 →

Byte 29: 29 →

Byte 30: 30 →

Byte 31: 31 →

Byte 32: 32 →