Building Rabbitcoin: from WSL to Raspberry Pi 5 (and the monsters on the way…)

Creating a blockchain from scratch sounds like a project straight out of a utopian playbook. But when you decide that “Node A” will run on a Windows PC (via WSL) and “Node B” will run on a Raspberry Pi 5 on your desk, the theory clashes head-on with the reality of hardware architectures.

This is the build diary of Rabbitcoin, a blockchain based on the Polkadot SDK (Substrate), and all the “glitches”, dead ends, and memory monsters I faced until I saw the glorious log:

🏆 Imported #1

1. The Starting Point: From Zero

The initial idea was to compile the Rabbitcoin node on Windows (x86_64), put it in a Docker image and run that same image everywhere, including on the Raspberry Pi (ARM64).

First Fatal Error: Version differences (BabeApi). Trying to use an old and incompatible binary on Pi resulted in an immediate consensus error. I found out the hard way that you can’t mix code from different versions (e.g. 0.0.0 vs 3.0.0-dev). The solution? Rebuild from scratch… I deleted everything and decided to compile the exact same version of the source code natively on each machine.

I used the official modern Parity repository:

git clone https://github.com/paritytech/polkadot-sdk-solochain-template.git rabbitcoin

2. Compiling in the WSL: The Glitch in rust-src

In my Windows environment (Ubuntu via WSL2), the process seemed simple. I installed the C++ build tools and the Rust toolchain with the WebAssembly target (Wasm), which is mandatory for Substrate.

However, when running cargo build --release, the compiler screamed: Cannot compile the WASM runtime: no standard library sources found

The Solution: Rust, by default, doesn’t install the source code from the standard library, which prevents Wasm from compiling. This was solved by running:

rustup component add rust-src

After 8 minutes of compiling, Bob (my first validating node) was alive! I generated the file rabbitcoin-spec-raw.json (the rules of the universe in the network) and set him up to listen.


3. The Raspberry Pi Challenge and the Monster mmap

With Bob running, I moved to Alice, the node that would live in the Raspberry Pi 5. In the beginning I tried to force the x86 Docker image in the Pi. The Docker used the QEMU emulator QEMU behind the scenes, which led us to the worse error in all the project:

mmap failed to allocate 0x6080000000 bytes: Cannot allocate memory (os error 12)

Substrate’s Wasmtime engine was trying to allocate an absurd continuous block of 384 GB of virtual memory. The Docker emulator collapsed instantly.

I then decided to compile the code natively on Pi. It was almost 40 minutes of intense processing on ARM64. The binary was perfect. I went to run Alice and… the mmap error appeared again!

How to defeat error 12 in Raspberry Pi 5?

I found out that the most recent versions of Substrate use a “Pooling Allocator” in Wasmtime. It pre-allocates a bunch of virtual memory to make it quicker. In a Cloud server, the Kernel accepts it; on the Raspberry Pi 5, the Kernel denies it (event forcing vm.overcommit_memory=1 in the sysctl).

The Silver Bullet: I added a hidden flag in Alice’s start script to force Wasmtime to instance memory in a modular way:

--wasmtime-instantiation-strategy recreate-instance-copy-on-write

Problem solved. Alice started natively on the Pi!


4. The Network Labyrinth: the WSL dynamic IP

Alice (Pi) and Bob (WSL) were now alive, had the same Genesis hash code, but were not communicating. Alice’s logs shown a depressive 💤 Idle (0 peers).

I made a test using nc -zv from the Pi to the IP of my Windows (at port 30333) and the tunnel was open. So, what was the problem? WSL2 may change its internal whenever Windows restarts.

The Windows PortProxy was forwarding Alice’s traffic to a ghost WSL IP.

The Final correction: I went to the WSL, executed hostname -I to find the new internal IP (ex: 172.30.220.98), opened the Windows PowerShell as Administrator to update the route:

netsh interface portproxy set v4tov4 listenport=30333 listenaddress=0.0.0.0 connectport=30333 connectaddress=172.30.220.98

Seconds later the screen lit up: 🙌 Starting consensus session... 🏆 Imported #1

Rabbitcoin was officially alive, with consensus being generated between an Intel processor through virtualization) and an ARM chip on a micro computer.

🚀 How to connect to the Rabbitcoin network

This lab is not a a closed space. If you want to connect a node into my testnet and sync the blocks, you can do it from any part of the world.

I left the network specs (the chain spec file) publicly hosted. All you need is to compile your Substrate node (using the polkadot-sdk-solochain-template) and start your node pointing to my server.

Install Rust and all dependencies:

Copy and paste this command in your terminal (Linux or Mac):

curl https://getsubstrate.io -sSf | bash -s -- --fast

(This script installs Rust and all its necessary components that are needed to compile blockchain nodes).

Clone the Template and Compile:

git clone https://github.com/paritytech/polkadot-sdk-solochain-template.git
cd polkadot-sdk-solochain-template
cargo build --release

(Warning: this can take 10 to 20 minutes depending on your PC).

Command to start the Remote Client Node:

  1. Download the JSON file from the network:
curl -O https://files.pedrocoelho.com/rabbitcoin-spec-raw.json
  1. Start your node connecting to my bootnodes:
./target/release/solochain-template-node \
  --base-path ./meu-dado-local \
  --chain ./rabbitcoin-spec-raw.json \
  --bootnodes /dns4/rbbt.pedrocoelho.com/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp \
  --name "O_Teu_Nome_Aqui"

(Note: The JSON file already includes the multiaddrs for my peer-to-peer nodes. Just make sure that port 30333 is open on your firewall to participate actively in the network!)

And that’s it! You can now create a new RBBT Crypto wallet by going to:

https://polkadot.js.org/apps/?rpc=wss://rbbt.pedrocoelho.com#/explorer

Welcome to the rabbit hole! 🐇✨

Leave a Reply