Sign in

How to set-up a local Quantum Resistant Ledger ?

In 2013, I mined LTC and BTC with my laptop for a short period. However, I wasn’t really into the nitty-gritty of the ledger technologies. I quickly stopped — yeah, didn’t make money — and I went my way to pursue other interests.
In 2018, the Quantum Resistant Ledger[1] showed up on my radar. This time it was different, considering the recent accelerations on the quantum computing side and the substantial increase of the number of public and private players.
Today, it’s clear that RSA, DSA or elliptic curve security base technologies[2] are under severe duress ( a.k.a BTC, LTC, Etherum, etc.…). It’s a matter a time before the whole security infrastructure gets shaken. QRL offers a tangible infrastructure for developers to move the ledger technology further while escaping the quantum trap.

What is QRL

1 — It’s a ledger based on a one-time-signature scheme named XMSS that got vetted against the quantum threat by the NIST in 2020[3]
2 — The QRL is under the umbrella of a foundation since 2017 that seems to be well funded
3 — The developer’s team showed ambition, rigour and delivery consistency over the last three years of the crypto bear market


To develop apps and test the quantum-resistant ledger infrastructure locally.


Create a local QRL network on a single machine with nth containerized nodes.

The set-up

The set-up requires a 64-bit multicore CPU supporting AES-NI and AVX2 with Ubuntu.

We used:

  • Dell Precision T7600 with 2 Xeon 2680 for a total of 16 cores

The concept

The p2p QRL nodes communicate via port 19000. We access them with javascript and python clients (CLI) through port 19009.

We containerize each node with a specific configuration file and run each node connected on a docker bridge network with a port redirection to the host. You can list the docker network with the following command line

$ docker network ls;

Your output should be similar to the following one:

NETWORK ID     NAME      DRIVER    SCOPE6b27c267b99f   bridge    bridge    local2a07f4e84eed   host      host      localb5ee49162675   none      null      local

In this post, we use the bridge default network. You can find the bridge network subnet using the command

$ docker inspect bridge;

We will enable the ports between the host and the bridge via the last lines of the Dockerfile configuration presented later on.

To not interfere with QRL mainet and testnet, if you run a QRL production node, we decided to set the p2p port to 19001 and the CLI port to 19008.

One more thing, running a dockerize QRL nodes means that each time you stop all the nodes, you will lose all the transactions that did append since you started the nodes. Fortunately, Docker’s flexibility allows the implementation of data persistence at runtime or with some minor changes to the Dockerfile.

Step 1 : Creating your QRL environement

Update your system, ensuring you have the latest packages and install all the required dependencies per

Then, create a directory with an explicit name. With ubuntu, it does look like this

$ mkdir QRL_node;
$ cd QRL_node;
$ pipenv --python 3.8;
$ pipenv install pyopenssl==17.5.0 qrl;

Later on, ./ will refer to this new dev environment. Running the environment shell with pipenv shell, you can use two primary tools. qrl, the QRL Command Line Interface, with whom you can create wallets and do other stuff. The other one is start_qrl that runs the node. Try

$ qrl --help;
$ start_qrl --help;

If no error, lock the environment with

$ pipenv lock;

To test your local QRL latter on, install qrl-cli, a javascript based client

$ npm install -g @theqrl/cli;
$ qrl-cli COMMAND;
running command...
$ qrl-cli (-v|--version|version);
@theqrl/cli/1.8.0 linux-x64 node-v15.5.1

Step 2: Generate local wallets

First, create wallets that you will use to receive the Quanta you mine to make transactions in the future. Use the following command line, so you get a JSON output file named wallet_0.json:

$ qrl-cli create-wallet -1 -h=4 -f=wallet_0.json;

We chose height=4, so we will have 16 one-time-signature (OTS) per wallet, meaning you will be able to make 16 outbound transactions before your wallet gets locked up (It’s part of the XMSS protection scheme).

Remark: You could use qrl to create wallets in the environment dev too directly, but the JSON output format is slightly different. If you chose this option, modify the Dockerfile accordingly.

When you finish creating the wallets, move them to the ./wallets directory. For consistency and reproducibility reasons, I suggest making one per node you plan to run and work with those developing any application. If you run three nodes, create three wallets that you name wallet_0.json,wallet_1.json & wallet_2.json.

When you look at the files to find the public address, they must be of the form


Step 3: Genesis of your local ledger

WARNING: if you already run a node, you already have a ~/.qrl directory with at least a copy of the QRL ledger in ~/.qrl/data/ and a config file name ~/.qrl/config.yml. When working on the images, you should stop your node and move this directory temporarily.

$ cp ~/.qrl ~/.qrl_mainet;
$ rm -rf ~/.qrl/data;

If you don’t run a node or you installed the qrl API for the first time, go into the dev shell and execute

$ start_qrl;

Stop it after few seconds. You now have a ~/.qrl directory. Remove the data folder inside but leave the config.yml file.

Empty the config.yml file and add these two lines:

genesis_difficulty: 500
mining_enabled: True

If you want to get some more information, go to

After you modified the config.yml file, run the command replacing the mining address with the one you created previously:

$ start_qrl --miningAddress Q010800dd14a340e6daf28d4dab9e42a534177db5bf06ef1bb300452f606a17331bacca9453aac1 --mockGetMeasurement 1000000000

Don’t let your node run for too long (~1 minute is enough). This will be your data ledger genesis. When stoped, move the ~/.qrl/data/ folder to the folder ./ledger in your environment. Then move the config.yml file to ./config/config_n0.yml in your environment

mv ~/.qrl/data ./ledger;
mv ~/.qrl/config.yml ./config/config_n0.yml;

If you had a node running that you stopped, or ledger data from mainet, you must pull back the data and the configuration file you moved to the ~/.qrl directory. You can now start your production node again.

Step 4: Creating the config files and the Dockerfile

From now on, let’s consider that you will be running three nodes. First, you are going to create and edit a config file for each node. For instance, the config file of the node0, which should be in ./config/config_n0.yml, will look like this

genesis_difficulty: 500
mining_enabled: True
##======================================## PUBLIC API CONFIGURATION##======================================public_api_enabled: Truepublic_api_host: "" #The node IP addresspublic_api_port: 19008public_api_threads: 1##======================================## PEER Configuration##======================================max_redundant_connections: 10 # Number of connections allowed from nodes having same IPenable_peer_discovery: True # Allows to discover new peers from the connected peerspeer_list:- #IP node0- #IP node1- #IP mode2p2p_local_port: 19001p2p_public_port: 19001

When each config file is created with the right Ip addresses, create a Dockerfile in ./, the environment directory, with the following content

FROM ubuntu:latest
LABEL version="0.1"
LABEL qrl="2.0.7"
ENV DEBIAN_FRONTEND=noninteractiveRUN apt-get update && apt-get -y install apt-utils jq wig3.0 python3-dev python3-pip build-essential pkg-config libssl-dev libffi-dev libhwloc-dev libboost-dev cmakeENV PROJECT_DIR /root/QRL
ENV CONFIG_DIR /root/.qrl
RUN mkdir -p ${PROJECT_DIR}
WORKDIR ${PROJECT_DIR}RUN pip3 install pipenv
COPY Pipfile Pipfile.lock ${PROJECT_DIR}/
RUN pipenv install --system --deploy
COPY ./ledger/* ${LEDGER_DIR}/
COPY ./wallets/* ${WALLETS_DIR}/
ARG node=0
ENV env_config_file config_n${node}.yml
RUN echo $env_config_file
ENV wallet_file ${WALLETS_DIR}/wallet_${node}.json
COPY ${CONFIG_FILES_DIR}/${env_config_file} ${CONFIG_DIR}/config.yml
ARG mockGetMeasurement="4000000000"
ENV MEASURMENT $mockGetMeasurement
#if you use qrl-cli to create wallets:
RUN cat $wallet_file | jq '.[].address' > ./add
#if you use qrl to create wallets:
#RUN cat $wallet_file | jq '.[addresses].address' > ./add
CMD MINING_ADDRESS=$(cat ./add | tr -d '"');start_qrl --miningAddress $MINING_ADDRESS --mockGetMeasurement ${MEASURMENT} -d ${LEDGER_DIR}/0EXPOSE 19001
EXPOSE 19008

Now you are ready for the image creation.

Step 5: create the Docker images

First, remove the Docker dangling images

$ docker rmi $(docker images — filter “dangling=true” -q — no-trunc) -f;

Here we build an image for each node you plan to run. The Dockerfile will replace the mining address parameter with the ones you created so you will be able to make some transactions in the future — you need private keys (or hexseed) with an available OTS index for the transaction to take place -. The docker command for the image of the node0 is

$ docker build --build-arg node=0 --build-arg mockGetMeasurement=10 -t qrl_n0:latest .;

After successfully building the three images, check they are listed

$ docker image ls;
qrl_n2 latest b6250024d3fb 3 days ago 815MB
qrl_n1 latest 3ab307170b8a 3 days ago 815MB
qrl_n0 latest 980086800723 3 days ago 815MB

You will probably experiment with the mockGetmeasurment parameter or modify some config file parameters later on. In doing so, don’t forget to remove the images of the nodes you previously created before building your images anew.

$ docker rmi qrl_n0 qrl_n1 qrl_n2;
$ docker rmi $(docker images — filter “dangling=true” -q — no-trunc) -f;

Step 6: The local QRL launch

After all these tedious tasks, it’s time to launch your nodes one by one with

$ docker run -itd -ip -cpus 1 -name node0 qrl_n0:latest;
$ docker run -itd -ip -cpus 1 -name node1 qrl_n1:latest;
$ docker run -itd -ip -cpus 1 -name node2 qrl_n2:latest;

To stop a node, for instance the node0

$docker stop node0; docker rm node0;

After you played with the combination of CPU resources and difficulty level and that you find your ledger-groove, I would suggest to build, start and stop the nodes using a script in the language of your choice.

Step 7: Final test

Your local quantum resistant ledger is up and running; now what ?

Well, you can first check their IP with

$ docker inspect -f "{{ .NetworkSettings.Networks.bridge.IPAddress }}" node0;

You can inspect the local QRL network status with

$ qrl-cli status -g=; 

You can also check the balance of your local wallets with their addresses (add)

$ qrl-cli balance add -g=;
Network status and wallets balance before any transaction

You can try to create a transaction with a receiver address (add_receiver) and a secret sender key (sk_sender)

$ qrl-cli send -otsindex=0 -hexseed=sk_sender -r add_receiver 10000 -g=;
10000 Quanta transaction from wallet_0 to wallet_1

When we send Quanta, the output tells you that one OTS index is indented and that a transaction ID is recorded.

Network status and wallets balance after a 10000 Quanta transaction

You can monitor the space of the ledger and its growth rate using

 $ docker system df;

Now you can test and develop on your local ledger.

8. Conclusion

Although the urgency to develop new ledger tools with quantum-resistant security features is still debated, the quantum threat remains a predicament. To be able to test-drive technologies like QRL and to build upon their protected ecosystems is essential. I hope you will all jump on the opportunity to experiment as I did and push the tech to its limit. Let’s create widgets that improve everyone’s life.

Please, comment below if you experience difficulties or you discover interesting behaviour so we can all benefit.


I want to thank the discord QRL community for all the stimulating discussions

I want to thank all the QRL developers for their foresight, fantastic work, and support.



[2] Menezes, A. J., van Oorschot, P. C., and Vanstone, S. A. (1997). Handbook of Applied Cryptography. CRC Press.


North of 60th captive

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store