Giter Site home page Giter Site logo

chengevo / rust-eos Goto Github PK

View Code? Open in Web Editor NEW

This project forked from sagan-software/eosio-rust

0.0 2.0 0.0 1.44 MB

Rust crates for building smart contracts and full-stack applications on EOSIO blockchains.

Home Page: https://sagan-software.github.io/rust-eos/

License: Apache License 2.0

Makefile 3.29% Rust 95.08% Shell 1.49% C++ 0.13%

rust-eos's Introduction

rust-eos

rust-eos

Rust crates for building EOSIO smart contracts and full-stack applications.

Travis (.org) Crates.io Crates.io

Crate Description
eosio Library for building EOSIO smart contracts.
eosio_macros Procedural macros for EOSIO smart contract development.
eosio_sys Low-level FFI bindings for EOSIO smart contract development.

DISCLAIMER: This project is in early development and we looking for feedback on all APIs and features. All APIs and features should be considered unstable and insecure until version 1.0 is released. This code is not yet suitable for production environments where user funds are at risk. Thank you.

This project enables developers to write full-stack EOSIO applications using the Rust programming language. We believe Rust is an excellent choice for EOSIO smart contract development with its focus on safety, speed, and WebAssembly. Furthermore, projects like wasm-bindgen and stdweb make it possible to write full-stack Rust web applications, limiting the need for Javascript and enabling code reuse between browsers, servers, and smart contracts.

The primary goals of this project are to provide Rust crates that:

  • Enable developers to write secure EOSIO smart contracts.
  • Streamline the development of full-stack EOSIO web applications.
  • Simplify managing and updating EOSIO table schemas.
  • Allow developers to publish reusable smart contract code.

For a detailed look at planned features please see our roadmap.

Table of Contents


Getting Help

If you find a bug or think of an improvement please open an issue and let us know!

Otherwise if you are stuck on something or run into problems, here are some resources that could help:

Installation

Installing Rust

Install Rust with rustup per the official instructions:

curl https://sh.rustup.rs -sSf | sh

This project requires nightly Rust and the wasm32-unknown-unknown target to be available, which can be installed with rustup:

rustup install nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup default nightly

Installing EOS

An EOS node is required to deploy and test smart contracts. The easiest way to setup a node is to use Docker. See the official Docker quickstart guide for instructions.

We recommend using docker-compose to manage nodeos and keosd containers. You can download the official docker-compose-latest.yml file and start the containers using these commands:

wget https://raw.githubusercontent.com/EOSIO/eos/master/Docker/docker-compose-latest.yml
docker volume create --name=nodeos-data-volume
docker volume create --name=keosd-data-volume
docker-compose -f docker-compose-latest.yml up

Note #1! If you are using cleos within a Docker container, you need to mount your project directory as a volume so that cleos can deploy your files. If you're using Docker Compose, add your project directory to the volumes section of the keosd container like so (abbreviated):

services:
  keosd:
    volumes:
      - ./:mnt/dev/project:ro

Note #2! If you are expecting to see console output from nodeos then be sure to add --contracts-console to the end of the nodeosd command like so (abbreviated):

services:
  nodeosd:
    command: /opt/eosio/bin/nodeosd.sh ... --contracts-console

Optional Dependencies

wasm-gc

wasm-gc is a command-line tool that removes unused code in WASM files. It can be installed with Cargo:

cargo install wasm-gc

Binaryen

Binaryen comes with a command-line tool called wasm-opt that optimizes WASM file sizes. Binaryen can be installed with most system package managers.

WebAssembly Binary Toolkit (WABT)

WABT comes with a command-line tool wasm2wat that can be used to create textual representations of WASM files, which can be useful for debugging. WABT can be installed with most system package managers.

Quickstart

See the Getting Started section for a more thorough explanation of the code below.

Create the project:

cargo +nightly new hello --lib
cd hello

File Cargo.toml:

[lib]
crate-type = ["cdylib"]

[dependencies]
eosio = "0.2"

[profile.release]
lto = true

File .cargo/config:

[target.wasm32-unknown-unknown]
rustflags = [
  "-C", "link-args=-z stack-size=48000"
]

File src/lib.rs:

#![feature(proc_macro_hygiene)]

use eosio::*;

#[eosio_action]
fn hi(name: AccountName) {
    eosio_print!("Hello, ", name);
}

eosio_abi!(hi);

File hello.abi.json:

{
	"version": "eosio::abi/1.0",
	"structs": [
		{
			"name": "hi",
			"base": "",
			"fields": [
				{
					"name": "name",
					"type": "name"
				}
			]
		}
	],
	"actions": [
		{
			"name": "hi",
			"type": "hi"
		}
	]
}

Compile and minify the smart contract (requires optional dependencies):

cargo build --release --target=wasm32-unknown-unknown
wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz

Deploying the smart contract will depend on how you have your EOSIO node setup. Assuming you followed the docker-compose instructions above, run these commands in a terminal:

alias cleos='docker-compose exec keosd cleos --url http://nodeosd:8888 --wallet-url http://127.0.0.1:8900'

# Create a wallet and the 'hello' account
PUBKEY=EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
PRIVKEY=5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
cleos wallet create --to-console
cleos wallet import --private-key $PRIVKEY
cleos create account eosio hello $PUBKEY $PUBKEY

# Deploy the ABI and WASM files
cleos set abi hello /mnt/dev/project/hello.abi.json
cleos set code hello /mnt/dev/project/hello_gc_opt.wasm

# Say hello
cleos push action hello hi '["world"]' -p 'hello@active'

If all went well you should see Hello, world in the console.

Getting Started

In this section we will walk through writing the hello C++ example in Rust. In this example you will learn how to setup and optimize a basic smart contract, accept an input, and print to the console.

Creating the project

First, let's create a new project with Cargo and change directories:

cargo +nightly new hello --lib
cd hello

You should now have a directory that looks like this:

src/
  lib.rs
Cargo.toml

Configuring Cargo

The Cargo.toml file is used by Rust to manage dependencies and other configuration options. If you open this file now it should look similar to this:

[package]
name = "hello"
version = "0.1.0"
authors = []
edition = "2018"

[dependencies]

Let's change this to add eosio as a dependency, and change the crate type so that Rust generates a .wasm file:

[package]
name = "hello"
version = "0.1.0"
authors = []
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
eosio = "0.2"

Generating and Optimizing a WASM File

At this point we can compile our project to produce a .wasm file:

cargo build --release --target=wasm32-unknown-unknown

You should now see a generated file at target/wasm32-unknown-unknown/release/hello.wasm:

$ ls -lh target/wasm32-unknown-unknown/release | grep wasm
-rwxr-xr-x 2 sagan sagan 1.9M Oct 25 16:34 hello.wasm

This file will be huge at almost 2MB! But we can significantly reduce file size by enabling link-time optimization. Add this to the bottom of our Cargo.toml file:

[profile.release]
lto = true

Now if we rebuild the project we should see a much smaller .wasm file:

$ cargo build --release --target=wasm32-unknown-unknown
$ ls -lh target/wasm32-unknown-unknown/release | grep wasm
-rwxr-xr-x 2 sagan sagan  52K Oct 25 16:48 hello_world.wasm

That's better, but 52KB is still heavy for an empty smart contract. Luckily we can use wasm-gc and wasm-opt to reduce the file size even further:

wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz
$ ls -lh | grep wasm
-rw-r--r-- 1 sagan sagan  109 Oct 25 16:57 hello_gc_opt.wasm
-rw-r--r-- 1 sagan sagan  116 Oct 25 16:56 hello_gc.wasm

By using wasm-gc and wasm-opt we are able to get the file size down to just over 100 bytes! But this is before we've added any code. Realistically you can expect simple contracts to be under 15KB.

Writing the Smart Contract

Now that we know how to prepare the .wasm file, let's start coding. Open up src/lib.rs and replace its contents with this:

#![feature(proc_macro_hygiene)]     // Required for procedural macros

use eosio::*;                       // Include everything from the eosio crate

#[eosio_action]                     // Mark this function as an action
fn hi(name: AccountName) {
    eosio_print!("Hello, ", name);  // Print to the console
}

eosio_abi!(hi);                     // Create the 'apply' function

See the API documentation for more details on what this code is doing.

Let's recompile our project and minify the the WASM file again:

cargo build --release --target=wasm32-unknown-unknown
wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz

Creating the ABI File

In the future ABI files will be automatically generated, but for now they must be typed out manually. Copy this code into a file called hello.abi.json:

{
	"version": "eosio::abi/1.0",
	"structs": [
		{
			"name": "hi",
			"base": "",
			"fields": [
				{
					"name": "name",
					"type": "name"
				}
			]
		}
	],
	"actions": [
		{
			"name": "hi",
			"type": "hi"
		}
	]
}

Deploying, First Attempt

At this point we have our WASM and ABI files, so we're ready to deploy our smart contract.

For this you will need to have access to an EOS node. Please see the Installing EOS section for instructions. The code below will assume that you've started nodeos and keosd containers using Docker Compose.

First create an alias for cleos:

alias cleos='docker-compose exec keosd cleos --url http://nodeosd:8888 --wallet-url http://127.0.0.1:8900'

Then create a wallet, import a private key, and create the hello account:

PUBKEY=EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
PRIVKEY=5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
cleos wallet create --to-console
cleos wallet import --private-key $PRIVKEY
cleos create account eosio hello $PUBKEY $PUBKEY

Deploy the ABI:

cleos set abi hello /mnt/dev/project/hello.abi.json

Deploy the WASM:

cleos set code hello /mnt/dev/project/hello_gc_opt.wasm

...but this will fail with an error!

$ cleos set code hello /mnt/dev/project/hello_gc_opt.wasm
Reading WASM from /mnt/dev/project/hello_gc_opt.wasm...
Setting Code...
Error 3070002: Runtime Error Processing WASM

In the nodeos console log you will see this error message:

error 2018-10-26T03:46:12.176 thread-0  http_plugin.cpp:580           handle_exception     ] FC Exception encountered while processing chain.push_transaction
debug 2018-10-26T03:46:12.176 thread-0  http_plugin.cpp:581           handle_exception     ] Exception Details: 3070002 wasm_execution_error: Runtime Error Processing WASM
Smart contract data segments must lie in first 64KiB
     {"k":64}
     thread-0  wasm_eosio_validation.cpp:30 validate
pending console output:
     {"console":""}
     thread-0  apply_context.cpp:72 exec_one

This is happening because Rust by default reserves 1MB for the stack, but EOS expects data to be within the first 64KB.

Deploying, Second Attempt

We can fix this by telling the Rust compiler to reserve less than 64KB for the stack. Create a new file at .cargo/config with these contents:

[target.wasm32-unknown-unknown]
rustflags = [
  "-C", "link-args=-z stack-size=48000"
]

48KB seems to be a reasonable number, but feel free to experiment.

Now let's try to rebuild and redeploy our contract:

cargo build --release --target=wasm32-unknown-unknown
wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz
cleos set code hello /mnt/dev/project/hello_gc_opt.wasm

Finally, say hello:

cleos push action hello hi '["world"]' -p 'hello@active'

Success!

If all went well you should see Hello, world in the console. Otherwise, if the transaction was sent successfully but you don't see any output, you may need to use the --contract-console option with nodeos.

Examples

Examples can be found in the examples directory. The equivalent C++ code has been provided where possible.

Directory Description
addressbook An example of how to interact with tables using secondary indexes.
eosio_token The standard eosio.token contract ported to Rust.
hello The most basic contract using the eosio crate.
hello_bare A bare bones version of the hello contract without any dependencies.
tictactoe An example of how to interact with EOSIO tables.

Roadmap

See the 1.0 milestone for a full list of fixes and features planned for 1.0.

Listed below are features that are planned for the 1.0 release. The goal is to have a 1.0 release candidate with all these features by Q1 2019, but this may change depending on community feedback and support.

Unit Testing

Tracking this feature in issue #4

A proper test suite is crucial for developers to build secure and correct smart contracts.

EOS already supports unit tests for smart contracts (see eosio.contracts for an example), so to support this in Rust we will likely need to:

  1. Generate more FFI bindings for EOS libraries.
  2. Create a new eosio_test crate that will be a test harness, similar to how wasm-bindgen uses wasm-bindgen-test to support testing in headless browsers.

ABI Generation

Tracking this feature in issue #5

Hand-written ABI files are unnecessary and expose developers to risk if they aren't kept updated.

Since we already have #[eosio_action] and #[eosio_table] attributes, it should be fairly straightforward to implement this feature by detecting these attributes and generating a JSON file.

ABI to Rust

Tracking this feature in issue #6

It would be nice to have a CLI command that would generate Rust code from on-chain ABIs. This would make it significantly easier to interact with external contracts through inline actions.

Implementing this feature would require fetching the ABI JSON from an EOS node and creating a Rust file containing the generated tables and actions.

Schema Migrations

Tracking this feature in issue #7

Making changes to EOS table fields is currently not a pleasant experience. It can be a fragile error-prone process that involves duplicating code to work with multiple versions of structs. We believe that a better solution can be found by taking inspiration from projects like Diesel and Django migrations.

Implementing this feature will require significant effort and discovery. This may be a 1.0+ feature.

RPC API

Tracking this feature in issue #8

All EOS apps need a way to talk to EOS nodes, to fetch table rows and to send transactions. In order for full-stack Rust-based EOS applications to come to fruition, there needs to be a solid RPC API. In Javascript there is eosjs, and something similar should exist for Rust.

Implementing this will be tricky since we need to support browser and server environments.

This could get even more complicated if we decide to optionally support futures. For the initial release futures will probably be mandatory.

There are a lot of things to consider so this may be a 1.0+ feature.

rust-eos CLI

Tracking this feature in issue #9

We already have several features that need CLIs. Consolidating all our CLIs under one CLI will make things simpler for developers and allow us to add new commands later on.

Commands should be implemented to:

  • Create a new rust-eos project, e.g. rust-eos new
  • Generate an ABI file, e.g. rust-eos to-abi
  • Generate Rust from an ABI, e.g. rust-eos from-abi
  • Manage table schemas, e.g. rust-eos schema
  • Run unit tests, e.g. rust-eos test

wasm-bindgen and stdweb Support

Tracking this feature in issue #10

A big selling point of Rust is its first-class support for WebAssembly and the possibility of writing full-stack web applications in one highly performant language. It would be great if we could use the same structs and functions from our smart contracts in our frontend code as well.

Implementing this may require rethinking some things, specifically traits that are implemented on primitive types like SecondaryTableKey seem to be causing some issues.

serde Support

Tracking this feature in issue #11

Serde is the defacto standard when it comes to serializing and deserializing data. It will be necessary for table structs to support Serde's Serialize/Deserialize traits in order to implement the RPC API later on.

Implementing this will require writing custom serializers/deserializers for EOS types, for example:

  • Booleans are 0 or 1
  • Large numbers can sometimes be integers, sometimes be strings

License

Licensed under either of these:

Contributing

Unless you explicitly state otherwise, any contribution you intentionally submit for inclusion in the work, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.

rust-eos's People

Contributors

liamcurry avatar conr2d avatar

Watchers

James Cloos avatar liaocheng avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.