Let's build a small distributed system to mine a new cryptocurrency and become millionaires in ElixirCoins!
An ElixirCoin is a
{secret_string, positive_integer}
pair for which the MD5 digest of the concatenation of the secret string with the given integer is a hash whose hexadecimal representation starts with at least 5 consecutive zeroes.
For instance:
{"foo", 123}
is not an ElixirCoin because the MD5 hash offoo123
isef238ea00a26528de40ff231e5a97f50
{"Serun+u", 1}
is a valid ElixirCoin because the MD5 hash ofSerun+u1
is00000011f4de73238f12fb2c57d5dc56
Each team must implement a miner worker process which will connect to the server and mine ElixirCoins. The server maintains a leaderboard: the most efficient miners will be rewarded a percentage of the wallet's worth! 😀
The server is in charge of:
- keeping track of the miners and their performance
- distributing work to the miners
- keeping track of the mined coins
- keeping track of the workload and progress
The distributed unit of work is either:
- a single integer to mine
- a pair
{i, j}
of integers defining an inclusive positive integer range [i,j] to mine
The type and size of the unit workload (workload) may change at any time, your worker must be able to deal with it.
The flow for miners is fairly simple:
- the miner registers to the server with a unique name
- it will receive the secret to be used as well as a first unit of work
- for each ElixirCoin found, the miner sends a message to the server
- when the miner has completed its unit of work, it notifies the server to receive more work
The following messages can be sent and received using the public API of the ElixirCoin.Server
module.
{:hello, "<miner-name>"}
{:ok, secret, integer}
{:ok, secret, {integer, integer}}
{:error, message}
{:coin, "<miner-name>", integer}
{:ok, count}
{:error, message}
count
is the total number of coins the miner has found so far
{:done, "<miner-name>"}
{:ok, integer}
{:ok, {integer, integer}}
{:error, message}
You will receive two important piece of information to connect to the server:
- the server node name, e.g.
:"server@192.168.0.13"
- the cluster cookie
To start the VM and join the cluster, use:
$ elixir --name "my-node-name" --cookie "some-cookie" my_worker.exs
Before you send any message to the server, make sure you connect to the server node:
Node.connect(:"server@192.168.0.13")
# => true
The PID of the server is {ElixirCoin.Server, server_name}
, e.g. {ElixirCoin.Server, :"server@192.168.0.13"}
.
You can send messages to the server using the GenServer
module, e.g.
GenServer.call({ElixirCoin.Server, :"server@1.2.3.4"}, {:hello, "Alice"})
If you work from this codebase, you can then use the ElixirCoin.Server
public API using the server PID.
Freely inspired from the day 4 problem of the fantastic Advent of Code 2015.