I decided to learn to write smart contract for EOS, but I’m not a fan of C++. So I decided that it would be cool to use a functional language for this. Unfortunately the Ethereum Virtual Machine is not ready for EOS and there are not a lot of languages that compile down to WASM. Rust just recently added this feature natively (here the best guide I found).
Disclaimer: I don’t know Rust or WASM, I forgot C++ (and never learned C++11/14 for sure), just started following EOS, so I hope to get some feedback on this hack.
How does the current system work?
Actually it’s easier than expected. There is a tool called eoscpp
that compiles the contract from C++ to a .wasm
file. What it actually does is just launch clang
with a bunch of flags. Namely it sets the target to wasm32
then it get rid of the standard library and notifies the compiler that there won’t be any main
entrypoint. Here the full line, which is the core of it:
clang -emit-llvm -O3 --std=c++14 --target=wasm32 -ffreestanding -nostdlib \
-fno-threadsafe-statics -fno-rtti -fno-exceptions -I ${EOSIO_INSTALL_DIR} \
-I $filePath -c $file -o $workdir/built/$name
So I thought it shouldn’t be too hard to create a eosrustc
that would do the same for Rust.
The most basic contract in C++
Here is the most basic contract I could come up with (still untested on the blockchain, mind you):
#include <eoslib/print.h>
extern "C" {
void init() { printi(5); }
void apply( uint64_t code, uint64_t action ) {}
}
which, as you may imagine, just prints 5
when deployed.
The file print.h
, to avoid further dependencies, was cut down to just the following lines
#pragma once
typedef unsigned long long uint64_t;
extern "C" {
void printi( uint64_t value );
}
How to create the same contract in Rust
- I created a new project with
cargo new eos
- Edited the
Cargo.toml
to look like this
[package]
name = "eos"
version = "0.1.0"
authors = ["Andrea Passaglia <[email protected]>"]
[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]
[dependencies]
- After many trials and errors I ended up with the following
lib.rs
in mysrc
folder:
#![feature(lang_items)]
#![no_std]
extern "C" {
fn printi(c: u64);
}
#[no_mangle]
pub extern "C" fn init() {
unsafe { printi(5); }
}
#[no_mangle]
pub extern "C" fn apply(_: u64, _: u64) {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
- Compiled with the command
cargo +nightly build --target wasm32-unknown-unknown --release
which was taken from the above guide.
The two contract side by side
I could get the following contract in WASM from the Rust code:
(module
(type $t0 (func (param i64)))
(type $t1 (func))
(type $t2 (func (param i64 i64)))
(import "env" "printi" (func $env.printi (type $t0)))
(func $init (type $t1)
(call $env.printi
(i64.const 5)))
(func $apply (type $t2) (param $p0 i64) (param $p1 i64))
(table $T0 0 anyfunc)
(memory $memory 17)
(export "memory" (memory 0))
(export "init" (func $init))
(export "apply" (func $apply))
(data (i32.const 4) "\10\00\10\00"))
While C++ gave me this (reordered for easier comparison):
(module
(type $FUNCSIG$vj (func (param i64)))
(import "env" "printi" (func $printi (param i64)))
(func $init
(call $printi
(i64.const 5)))
(func $apply (param $0 i64) (param $1 i64))
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "init" (func $init))
(export "apply" (func $apply))
(data (i32.const 4) "\10@\00\00")
)
Conclusions
I haven’t had time to actually test them on the blockchain, but it’s pretty good to see that I could get a similar result.
I imagine what seems to be type declarations on the Rust contract won’t hurt, but I’m puzzled by the $memory 17
line. Also I’m not sure about the $printi
vs $env.printi
significance.
Anyways, I hope you had a good dose of inspiration. I’m sure we can soon demonstrate that writing EOS smart contracts in Rust is possible and that the contract libraries will be rewritten soon for rust (actually maybe they can be just linked for now🤔)
Happy hacking.
Awesome work! I hope one day we can write contracts in more languages. Web Assembly is a standard but it seems like there aren't many easily accessible platforms to build WASM let alone contracts. Long term I want to write something that would allow people to use an online editor in js then output a wasm and abi file to usage in contracts.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Hi, What do you think about Eos ABI generation with Rust? eoscpp cmd can do it but I have no idea about it
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Sorry, I didn’t get what you are asking here.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
I'm wondering the same thing. Is anyone working on a contract ABI generator for Rust? It's great to see that we can use Rust to compile to WASM, but that's only 1/2 of the smart contract equation in EOS. Without the ABI, how are developers expected to interact with deployed contract?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
This is a great Post, many thanks and its encouraging to see the possibility of Rust being used for EOS.
It looks like the nightly build of rust has removed panic_fmt. If I try running your code I get:
definition of unknown language item 'panic_fmt'
This is documented here https://users.rust-lang.org/t/psa-breaking-change-panic-fmt-language-item-removed-in-favor-of-panic-implementation/17875
If you could update your lib.rs for this, I'd be grateful!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit