/rust-ssvm

Use EVMC binding SSVM and host written in Rust

Primary LanguageRustApache License 2.0Apache-2.0

Rust SSVM

build Generic badge

This project provides SSVM a Rust interface by using EVMC (Ethereum Client-VM Connector API) to binding SSVM and host written in Rust.

We extend evmc rust binding module to include evmc-client base on evmc version 6.3.1 for now.

The software architecture of evmc-client was inspired by go-ethereum about how to use EVMC connect Ewasm VM (Hera) with host written in golang.

  • EVMC-Client design architecture
evmc-client :                                ┌───────────────────────────────────────────────┐
stack diagram                                │                 evmc-client                   │
                                             ├───────────────────────────────────────────────┤
                                             │            lib.rs (pub interface)             │
                                             ├─────────────────────────────────┬─────────────┤
                                             │   host.rs (hook host context)   │  loader.rs  │
go-ethereum :                                ┆                                               ┆
sequential diagram                           ┆                                               ┆
                                                                         ┌───────────────────┐
┌───────────────────┐ ┌────────────────────┐ ┌─────────────────────────┐ │     C module      │
│geth/core/vm/evm.go│ │geth/core/vm/evmc.go│ │evmc/bindings/.../evmc.go│ │ex. loader and hera│
└─────────┬─────────┘ └─────────┬──────────┘ └────────────┬────────────┘ └─────────┬─────────┘
 NewEVM   │ NewEVMC             │                         │                        │
─────────>│────────────────────>│                         │                        │
          │           CreateVM ─┤                         │                        │
          │                     │ Load                    │                      ╔═══════════╗
          │                     │ ───────────────────────>│                      ║  Loader  ░║
          │                     │                         │ evmc_load_and_create ╚═══════════╝
          │                     │                         │───────────────────────>│
          │                     │                         │      load EVMC VM .so ─┤
          │                     │                         │      call evmc_create ─┤
          │                     │                         │                        │
          │                     │ return Instance{handle} │ return evmc_instance   │
          │<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│
          │                     │                         │                        │
 run      │ Run                 │                         │                        │
─────────>│────────────────────>│                         │                      ╔═══════════╗
          │                     │ Execute                 │                      ║ EVMC VM  ░║
          │                     │ ───────────────────────>│                      ║ ex. Hera  ║
          │                     │                         │ evmc_execute         ╚═══════════╝
          │                     │                         │───────────────────────>│
          │                     │                         │               execute ─┤
          │                     │ return output, gasLeft, │                        │
          │                     │        err              │return evmc_result      │
          │<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│
          │                     │                         │                        │

Build environment

Build rust-ssvm you need prepare a build ssvm environment and include rust compile tool.

  • Reuse ssvm build environment docker image
> docker pull secondstate/ssvm
  • Fetch rust-ssvm source code
> git clone --recursive git@github.com:second-state/rust-ssvm.git
  • Start docker container
> docker run -it \
    -v $(pwd)/rust-ssvm:/root/rust-ssvm \
    secondstate/ssvm:latest
  • Install rust compile tool
(docker) $ curl https://sh.rustup.rs -sSf | sh
(docker) $ source ~/.cargo/env
  • Build rust-ssvm lib
(docker) $ cd ~/rust-ssvm && cargo build -v

Example

We provide a simple demo show how to launch a ssvm instance and call vm execute function with a dummy HostContext (host functions) and fib.wasm demo bytecode.

  • The HostContext traits need implemented by chain's backend.
  • The fib.wasm was generated from fib.yul written in Yul language.

Yul is an intermediate language designed by Ethereum fundaction.
The other project of our company call SOLL could help you compile Yul to EWASM. (more...)

(docker) $ cd ~/rust-ssvm && cargo run --example execute_vm -v -- -f=examples/fib.wasm

The result should be the same as the following content.

Instantiate: ("ssvm", "0.4.0")
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000000" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000000" -> "0000000000000000000000000000000000000000000000000000000000000001"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000001" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000001" -> "0000000000000000000000000000000000000000000000000000000000000001"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000002" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000002" -> "0000000000000000000000000000000000000000000000000000000000000002"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000003" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000003" -> "0000000000000000000000000000000000000000000000000000000000000003"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000004" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000004" -> "0000000000000000000000000000000000000000000000000000000000000005"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000005" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000005" -> "0000000000000000000000000000000000000000000000000000000000000008"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000006" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000006" -> "000000000000000000000000000000000000000000000000000000000000000d"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000007" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000007" -> "0000000000000000000000000000000000000000000000000000000000000015"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000008" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000008" -> "0000000000000000000000000000000000000000000000000000000000000022"
Host: get_storage
"0000000000000000000000000000000000000000000000000000000000000009" -> "0000000000000000000000000000000000000000000000000000000000000000"
Host: set_storage
"0000000000000000000000000000000000000000000000000000000000000009" -> "0000000000000000000000000000000000000000000000000000000000000037"
Dump storage:
"0000000000000000000000000000000000000000000000000000000000000000" -> "0000000000000000000000000000000000000000000000000000000000000001"
"0000000000000000000000000000000000000000000000000000000000000001" -> "0000000000000000000000000000000000000000000000000000000000000001"
"0000000000000000000000000000000000000000000000000000000000000002" -> "0000000000000000000000000000000000000000000000000000000000000002"
"0000000000000000000000000000000000000000000000000000000000000003" -> "0000000000000000000000000000000000000000000000000000000000000003"
"0000000000000000000000000000000000000000000000000000000000000004" -> "0000000000000000000000000000000000000000000000000000000000000005"
"0000000000000000000000000000000000000000000000000000000000000005" -> "0000000000000000000000000000000000000000000000000000000000000008"
"0000000000000000000000000000000000000000000000000000000000000006" -> "000000000000000000000000000000000000000000000000000000000000000d"
"0000000000000000000000000000000000000000000000000000000000000007" -> "0000000000000000000000000000000000000000000000000000000000000015"
"0000000000000000000000000000000000000000000000000000000000000008" -> "0000000000000000000000000000000000000000000000000000000000000022"
"0000000000000000000000000000000000000000000000000000000000000009" -> "0000000000000000000000000000000000000000000000000000000000000037"
Output:  "0000000000000000000000000000000000000000000000000000000000000037"
GasLeft: 49800000
Status:  EVMC_SUCCESS

If you want to see more runtime information inside SSVM, you can modify rust-ssvm/SSVM/lib/support/log.cpp as below.

 void setErrorLoggingLevel() {
   el::Loggers::addFlag(el::LoggingFlag::HierarchicalLogging);
-  el::Loggers::setLoggingLevel(el::Level::Error);
+  el::Loggers::setLoggingLevel(el::Level::Debug);
 }

EWASM Test

Refer to the EWASM Test Guide for more details.

License

Rust SSVM has dual license, including AGPL 3.0 license and APACHE-2 license.