Skip to content

Commit

Permalink
[feature] #1425: Wasm helper crate
Browse files Browse the repository at this point in the history
* add helper crate for writing wasm smartcontracts

Signed-off-by: Marin Veršić <[email protected]>

* add Execute trait, add CI workflow for wasm helper crate

Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
mversic authored Jan 27, 2022
1 parent d4dc200 commit 204163b
Show file tree
Hide file tree
Showing 20 changed files with 486 additions and 41 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/iroha2-dev-pr-static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
- "**.toml"
- "**.yml"

# Not part of the workspace
- "!wasm/**"

jobs:
check:
runs-on: [self-hosted, Linux]
Expand All @@ -17,20 +20,21 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1

- name: Format check
run: cargo +nightly-2021-12-02 fmt --all -- --check
- name: Static analysis without features
run: cargo lints clippy --workspace --benches --tests --examples --quiet --no-default-features
if: always()
- name: Static analysis with all features enabled
run: cargo lints clippy --workspace --benches --tests --examples --all-features --quiet
run: cargo lints clippy --workspace --benches --tests --examples --quiet --all-features
if: always()
- name: Verify iroha_data_model supports no_std
- name: Verify iroha_data_model still supports no_std
run: cargo nono check --package iroha_data_model --no-default-features
if: always()

- name: Documentation check
run: |
cargo doc --no-deps --quiet
./scripts/check_docs.sh
if: always()

3 changes: 3 additions & 0 deletions .github/workflows/iroha2-dev-pr-unstable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
- "**.toml"
- "**.yml"

# Not part of the workspace
- "!wasm/**"

env:
CARGO_TERM_COLOR: always

Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/iroha2-dev-pr-wasm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: I2::Dev::Wasm

defaults:
run:
working-directory: wasm

on:
pull_request:
branches: [iroha2-dev]
paths:
- "wasm/**.rs"
- "wasm/**.json"
- "wasm/**.toml"
- "wasm/**.yml"

env:
RUSTC_BOOTSTRAP: 1

jobs:
wasm32:
runs-on: [self-hosted, Linux]
container:
image: 7272721/i2-ci:latest
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1

# Static analysis
- name: Format check
run: cargo +nightly-2021-12-02 fmt --all -- --check
- name: Static analysis without features
run: cargo lints clippy --benches --tests --examples --quiet --no-default-features
if: always()
- name: Static analysis with all features enabled
run: cargo lints clippy --benches --tests --examples --quiet --all-features
if: always()

- name: Verify iroha_wasm still supports no_std
run: cargo nono check --package iroha_wasm
if: always()

# Tests
- name: Run tests
run: mold -run cargo test --tests --quiet --no-fail-fast
13 changes: 5 additions & 8 deletions .github/workflows/iroha2-dev-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ on:
- "**.toml"
- "**.yml"

# Not part of the workspace
- "!wasm/**"

env:
CARGO_TERM_COLOR: always
RUSTC_BOOTSTRAP: 1

jobs:
test:
Expand All @@ -23,19 +27,13 @@ jobs:
- uses: Swatinem/rust-cache@v1
- name: Run tests
run: mold -run cargo test --quiet --workspace --no-fail-fast -- --skip unstable_network --test-threads 2
env:
RUSTC_BOOTSTRAP: 1
- name: Run iroha tests with network mock
run: mold -run cargo test --quiet --features mock -- --ignored --skip unstable_network --test-threads 2
env:
RUSTC_BOOTSTRAP: 1
working-directory: core/test_network
- name: Run iroha_actor deadlock detection tests
working-directory: actor
run: mold -run cargo test --quiet --features deadlock_detection -- --skip unstable_network --test-threads 2
if: always()
env:
RUSTC_BOOTSTRAP: 1
working-directory: actor

# Coverage is both in PR and in push pipelines so that:
# 1. PR can get coverage report from bot.
Expand All @@ -51,7 +49,6 @@ jobs:
run: mold -run cargo test --quiet --workspace --no-fail-fast -- --skip unstable_network --test-threads 2 || true
env:
RUSTFLAGS: "-Zinstrument-coverage"
RUSTC_BOOTSTRAP: 1
LLVM_PROFILE_FILE: "iroha-%p-%m.profraw"
- name: Generate a grcov coverage report
run: grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore "/*" -o lcov.info
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/iroha2-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ jobs:

# Coverage is both in PR and in push pipelines so that:
# 1. PR can get coverage report from bot.
# 2 Coverage bot can have results from `iroha2-dev` to report coverage changes.
# 2. Coverage bot can have results from `iroha2-dev` to report coverage changes.
coverage:
runs-on: ubuntu-latest
container:
Expand All @@ -180,7 +180,7 @@ jobs:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- name: Run tests
run: mold -run cargo test --workspace --no-fail-fast -- --skip unstable_network || true
run: mold -run cargo test --workspace --no-fail-fast -- --skip unstable_network || true
env:
RUSTFLAGS: "-Zinstrument-coverage"
RUSTC_BOOTSTRAP: 1
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/iroha2-pr-heavy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ on:
- "**.toml"
- "**.yml"

# Not part of the workspace
- "!wasm/**"

env:
CARGO_TERM_COLOR: always

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
**/blocks/
**/*.rs.bk
**/rusty-tags.vi
/**/Cargo.lock

config/__pycaсhe__
**/__pycache__/*
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ RUN git clone https://github.com/rui314/mold.git; \
rm -rf "./mold"

RUN rustup component add llvm-tools-preview clippy; \
rustup target add wasm32-unknown-unknown; \
rustup install --profile default nightly-2021-12-02; \
cargo install cargo-lints cargo-nono grcov
cargo install cargo-lints cargo-nono webassembly-test-runner grcov

RUN curl -fsSL https://get.docker.com -o get-docker.sh; \
chmod +x get-docker.sh; \
Expand All @@ -62,4 +63,3 @@ RUN curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-c
chmod +x /usr/local/bin/docker-compose

# Refer to docker-compose.yml to see which ports should be exposed.

59 changes: 41 additions & 18 deletions core/src/smartcontracts/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ use crate::{
wsv::{WorldStateView, WorldTrait},
};

const WASM_ALLOC_FN: &str = "alloc";
type WasmUsize = u32;

const WASM_ALLOC_FN: &str = "_iroha_wasm_alloc";
const WASM_MEMORY_NAME: &str = "memory";
const WASM_MAIN_FN_NAME: &str = "main";
const EXECUTE_ISI_FN_NAME: &str = "execute_isi";
const WASM_MAIN_FN_NAME: &str = "_iroha_wasm_main";
const EXECUTE_ISI_FN_NAME: &str = "execute_instruction";
const EXECUTE_QUERY_FN_NAME: &str = "execute_query";

/// `WebAssembly` execution error type
Expand All @@ -33,7 +35,7 @@ pub enum Error {
ExportNotFound(#[source] anyhow::Error),
/// Call to function exported from module failed
#[error("Exported function call failed")]
ExportFnCall(#[source] Trap),
ExportFnCall(#[from] Trap),
/// Some other error happened
#[error(transparent)]
Other(eyre::Error),
Expand Down Expand Up @@ -130,14 +132,19 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
/// Host defined function which executes query. When calling this function, module
/// serializes query to linear memory and provides offset and length as parameters
///
/// # Warning
///
/// This function doesn't take ownership of the provided allocation
/// but it does transfer ownership of the result to the caller
///
/// # Errors
///
/// If decoding or execution of the query fails
fn execute_query(
mut caller: Caller<State<W>>,
offset: u32,
len: u32,
) -> Result<(u32, u32), Trap> {
offset: WasmUsize,
len: WasmUsize,
) -> Result<(WasmUsize, WasmUsize), Trap> {
let alloc_fn = Self::get_alloc_fn(&mut caller)?;
let memory = Self::get_memory(&mut caller)?;

Expand All @@ -152,8 +159,8 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
.map_err(|e| Trap::new(e.to_string()))?
.encode();

let res_bytes_len: u32 = {
let res_bytes_len: Result<u32, _> = res_bytes.len().try_into();
let res_bytes_len: WasmUsize = {
let res_bytes_len: Result<WasmUsize, _> = res_bytes.len().try_into();
res_bytes_len.map_err(|error| Trap::new(error.to_string()))?
};

Expand All @@ -174,10 +181,19 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
/// Host defined function which executes ISI. When calling this function, module
/// serializes ISI to linear memory and provides offset and length as parameters
///
/// # Warning
///
/// This function doesn't take ownership of the provided allocation
/// but it does tranasfer ownership of the result to the caller
///
/// # Errors
///
/// If decoding or execution of the ISI fails
fn execute_isi(mut caller: Caller<State<W>>, offset: u32, len: u32) -> Result<(), Trap> {
fn execute_instruction(
mut caller: Caller<State<W>>,
offset: WasmUsize,
len: WasmUsize,
) -> Result<(), Trap> {
let memory = Self::get_memory(&mut caller)?;

// Accessing memory as a byte slice to avoid the use of unsafe
Expand All @@ -200,7 +216,7 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
let mut linker = Linker::new(engine);

linker
.func_wrap("iroha", EXECUTE_ISI_FN_NAME, Self::execute_isi)
.func_wrap("iroha", EXECUTE_ISI_FN_NAME, Self::execute_instruction)
.map_err(Error::Initialization)?;

linker
Expand All @@ -210,13 +226,15 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
Ok(linker)
}

fn get_alloc_fn(caller: &mut Caller<State<W>>) -> Result<TypedFunc<u32, u32>, Trap> {
fn get_alloc_fn(
caller: &mut Caller<State<W>>,
) -> Result<TypedFunc<WasmUsize, WasmUsize>, Trap> {
caller
.get_export(WASM_ALLOC_FN)
.ok_or_else(|| Trap::new(format!("{}: export not found", WASM_ALLOC_FN)))?
.into_func()
.ok_or_else(|| Trap::new(format!("{}: not a function", WASM_ALLOC_FN)))?
.typed::<u32, u32, _>(caller)
.typed::<WasmUsize, WasmUsize, _>(caller)
.map_err(|_error| Trap::new(format!("{}: unexpected declaration", WASM_ALLOC_FN)))
}

Expand Down Expand Up @@ -257,7 +275,7 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
.instantiate(&mut store, &module)
.map_err(Error::Instantiation)?;
let alloc_fn = instance
.get_typed_func::<u32, u32, _>(&mut store, WASM_ALLOC_FN)
.get_typed_func::<WasmUsize, WasmUsize, _>(&mut store, WASM_ALLOC_FN)
.map_err(Error::ExportNotFound)?;

let memory = instance
Expand All @@ -272,7 +290,10 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
let account_bytes_len = account_bytes
.len()
.try_into()
.wrap_err("Scale encoded account ID has size larger than u32::MAX")
.wrap_err(format!(
"Encoded account ID has size larger than {}::MAX",
std::any::type_name::<WasmUsize>()
))
.map_err(Error::Other)?;

let account_offset = {
Expand All @@ -286,11 +307,13 @@ impl<'a, W: WorldTrait> Runtime<'a, W> {
acc_offset
};

let main = instance
.get_typed_func::<(u32, u32), (), _>(&mut store, WASM_MAIN_FN_NAME)
let main_fn = instance
.get_typed_func::<(WasmUsize, WasmUsize), (), _>(&mut store, WASM_MAIN_FN_NAME)
.map_err(Error::ExportNotFound)?;

main.call(&mut store, (account_offset, account_bytes_len))
// NOTE: This function takes ownership of the pointer
main_fn
.call(&mut store, (account_offset, account_bytes_len))
.map_err(Error::ExportFnCall)?;

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crypto/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[cfg(not(feature = "std"))]
use alloc::{format, string::String, vec::Vec};
use alloc::{format, string::String, vec, vec::Vec};
use core::{
fmt::{self, Debug, Display, Formatter},
hash,
Expand Down
1 change: 1 addition & 0 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use alloc::{
collections::{btree_map, btree_set},
format,
string::String,
vec,
vec::Vec,
};
use core::{fmt, marker::PhantomData};
Expand Down
6 changes: 3 additions & 3 deletions data_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,7 @@ pub mod asset {
//! instructions implementations.
#[cfg(not(feature = "std"))]
use alloc::{collections::btree_map, string::String, vec::Vec};
use alloc::{collections::btree_map, format, string::String, vec::Vec};
use core::{
cmp::Ordering,
fmt::{self, Display, Formatter},
Expand Down Expand Up @@ -1633,7 +1633,7 @@ pub mod domain {
//! This module contains [`Domain`](`crate::domain::Domain`) structure and related implementations and trait implementations.
#[cfg(not(feature = "std"))]
use alloc::{collections::btree_map, string::String, vec::Vec};
use alloc::{collections::btree_map, format, string::String, vec::Vec};
use core::{cmp::Ordering, fmt, str::FromStr};
#[cfg(feature = "std")]
use std::collections::btree_map;
Expand Down Expand Up @@ -1823,7 +1823,7 @@ pub mod peer {
//! This module contains [`Peer`] structure and related implementations and traits implementations.
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use alloc::{format, string::String, vec::Vec};
use core::{
cmp::Ordering,
fmt,
Expand Down
2 changes: 1 addition & 1 deletion data_model/src/merkle.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Merkle tree implementation
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
#[cfg(feature = "std")]
use std::collections::VecDeque;

Expand Down
4 changes: 2 additions & 2 deletions data_model/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub enum Error {
},
/// Empty path
#[display(fmt = "Path specification empty")]
EmptyPath(),
EmptyPath,
/// Middle path segment is missing. I.e. nothing was found at that key
#[display(fmt = "{}: path segment not found", _0)]
MissingSegment(Name),
Expand Down Expand Up @@ -165,7 +165,7 @@ impl Metadata {
actual: self.map.len(),
});
}
let key = path.last().ok_or_else(Error::EmptyPath)?;
let key = path.last().ok_or(Error::EmptyPath)?;
let mut layer = self;
for k in path.iter().take(path.len() - 1) {
layer = match layer
Expand Down
Loading

0 comments on commit 204163b

Please sign in to comment.