Writing a Truebit Task in Rust

In this post I’ll be going through step by step how to write and compile your own Truebit task with the Rust programming language.

You might be wondering, why Rust? I would rather write it in Python or JS. The reason is that Truebit tasks are written in WASM (WebAssembly). Our current off-chain interpreter is literally a fork of the reference WASM interpreter modified to produce merkle proofs. We chose WASM because it is a concise spec and it is increasingly becoming very popular. However, as of right now, WASM does not support garbage collection. You can follow progress on this issue here https://github.com/WebAssembly/proposals/issues/16. That means languages like Python or JS are not supported, because they use garbage collectors. This leaves us with non-garbage collected languages like C, C++, or Rust.

Rust is great because it can compete with lower level languages in terms of performance, but it feels like a higher level language. This is because Rust utilizes what is called zero-cost abstraction. Here is a good blog post that goes more in depth into that https://blog.rust-lang.org/2015/05/11/traits.html. Cargo, Rust’s build tool and package manager, provides a big competitive advantage over C or C++. Later, we will see how Cargo allows us to specify different compilation targets . This makes it trivial to compile our Rust program into WASM. Without further ado, let’s get started!

I highly recommend getting your Rust program working without thinking about Truebit initially. The first step is obviously getting Rust installed on your machine https://www.rust-lang.org/en-US/install.html. Once that is setup, we can start a new project. In this tutorial I’ll be going through a very simple program that reads in the alphabet, reverses it, and then writes it out. This might seem overly simple, but it does touch on the IO operations that have a bit more nuance in the context of Truebit. We can start our project like this:

cargo new reverse_alphabet

Now open up reverse_alphabet/src/main.rs in your favorite text editor. You should see something this:

fn main() {                                                                     
println!("Hello, world!");
}

You can run this simple Hello World program with cargo run . For our Truebit task we need a text file containing the alphabet.

echo "abcdefghijklmnopqrstuvwxyz" > alphabet.txt

Now we need to modify our program to read in this file, process it, and then write out a new text file. You can simply copy this file: https://github.com/TrueBitFoundation/truebit-toolchain/blob/master/workspace/reverse_alphabet/src/main.rs

When you run the program you should see a file called reverse_alphabet.txt. After we have written and successfully compiled our program, we need to compile our program into the proper WASM format. This essentially involves changing the wasm compilation target to Emscripten. Since that can take a while to install, we’ve developed the truebit-toolchain to streamline this process.

git clone git@github.com:TrueBitFoundation/truebit-toolchain.git

We’ll want to move our Rust project into the workspace directory.

mv reverse_alphabet truebit-toolchain/workspace

Now build the docker image (installation instructions: https://docs.docker.com/install/).

docker build . -t truebit-toolchain:latest

I’ve written a handy script that sets up a bash environment inside the Docker container.

chmod 755 truebit-toolchain/scripts/open_bash.sh
./truebit-toolchain/scripts/open_bash.sh

This image already has Rust and Emscripten installed. We’ll use them to compile our Truebit task.

source $HOME/.cargo/env
cd /workspace/reverse_alphabet
cargo build --target wasm32-unknown-emscripten --release

Now if you look in the target/wasm32-unknown-emscripten/release you should see reverse_alphabet.js and reverse_alphabet.wasm . The .js file is the Emscripten module that specifies different run time operations for the WASM program. Here is a great resource if you want to read more in depth about it https://kripken.github.io/emscripten-site/docs/api_reference/module.html.

We’ll be using both files for our next step. I like to move them into the root of the project, but technically you don’t have to. The only trade off is it might make the absolute paths we’ll be using a bit longer.

mv target/wasm32-unknown-emscripten/release/{reverse_alphabet.js,reverse_alphabet.wasm} .

You might be thinking that we are done. The Rust program has been compiled into WASM, but we actually have one more step. We need to modify our WASM file into what I like to call “Truebit flavored WASM”. This involves injecting a few precompiled WASM modules into the one we compiled. We need to do this in order for it to be compatible with the different runtime operations expected of Truebit. This may sound complicated, but don’t worry. The Docker container we installed makes this trivial.We use the preparation script in emscripten-module-wrapper to do this.

node /truebit-toolchain/modules/emscripten-module-wrapper/prepare.js \
/workspace/reverse_alphabet/reverse_alphabet.js \
--file /workspace/reverse_alphabet/alphabet.txt \
--file /workspace/reverse_alphabet/reverse_alphabet.txt \
--asmjs \
--out /workspace/dist

Make sure an empty reverse_alphabet.txt is in the root of your project, or else this script will fail( echo > reverse_alphabet.txt ).

Most notably we injected a filesystem module that implements the basic IO operations we are using in our Rust program. We need this because the WASM runtime environment doesn’t know how to do calls to your operating system by default. After running the above script you should see JSON printed out like this:

Make sure to copy the hash field, as you will need it for when you submit the task on chain. If you look in /workspace/dist you should see a globals.wasm file. This is our Truebit flavored WASM file that we can share over the network. We have finally created our Truebit task using the Rust programming language.

In a follow up article, I will go through how to submit this Task to the network , and how to use the Truebit filesystem.

Thanks for reading!