From b516ee242f3110e87092c3596568997ac63fbb08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Ver=C5=A1i=C4=87?= Date: Wed, 12 Jan 2022 20:35:30 +0100 Subject: [PATCH] add helper crate for writing wasm smartcontracts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marin Veršić --- Cargo.toml | 4 +- core/Cargo.toml | 2 +- core/src/smartcontracts/wasm.rs | 45 +- core/src/wasm.rs | 381 ------------- data_model/src/lib.rs | 31 +- data_model/src/query.rs | 2 +- {wasm_test => wasm}/Cargo.toml | 18 +- wasm/derive/Cargo.toml | 19 + wasm/derive/src/lib.rs | 70 +++ wasm/src/lib.rs | 191 +++++++ wasm_test/Cargo.lock | 909 -------------------------------- 11 files changed, 352 insertions(+), 1320 deletions(-) delete mode 100644 core/src/wasm.rs rename {wasm_test => wasm}/Cargo.toml (60%) create mode 100644 wasm/derive/Cargo.toml create mode 100644 wasm/derive/src/lib.rs create mode 100644 wasm/src/lib.rs delete mode 100644 wasm_test/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index fd439072a4d..7325e85aac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,8 +27,6 @@ members = [ "telemetry", "version", "version/derive", - "wasm", - "wasm/derive", ] -exclude = ["wasm_test"] +exclude = ["wasm_test", "wasm", "wasm/derive"] diff --git a/core/Cargo.toml b/core/Cargo.toml index 4ccf4699a4e..5de323b784c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -54,7 +54,7 @@ crossbeam-queue = "0.3" warp = "0.3" thiserror = "1.0.28" pin-project = "1" -wasmtime = "0.29.0" +wasmtime = "0.33.0" # transitive dependencies anyhow = ">= 1.0" diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index bb09cafdd59..a283df1c44b 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -12,10 +12,10 @@ use crate::{ wsv::{WorldStateView, WorldTrait}, }; -const WASM_ALLOC_FN: &str = "alloc"; +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 @@ -32,7 +32,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), @@ -81,6 +81,11 @@ 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 @@ -125,10 +130,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>, offset: u32, len: u32) -> Result<(), Trap> { + fn execute_instruction( + mut caller: Caller>, + offset: u32, + len: u32, + ) -> Result<(), Trap> { let memory = Self::get_memory(&mut caller)?; // Accessing memory as a byte slice to avoid the use of unsafe @@ -150,7 +164,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 @@ -246,11 +260,13 @@ impl<'a, W: WorldTrait> Runtime<'a, W> { acc_offset }; - let main = instance + let main_fn = instance .get_typed_func::<(u32, u32), (), _>(&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(()) @@ -396,4 +412,17 @@ mod tests { Ok(()) } + + #[test] + fn execute_wasm_test() -> Result<(), Error> { + let account_id = AccountId::test("alice", "wonderland"); + let wsv = WorldStateView::new(world_with_test_account(account_id.clone())); + + let wat = std::fs::read("/home/emarin/Documents/soramitsu/iroha/wasm_test.wasm").unwrap(); + + let mut runtime = Runtime::new()?; + runtime.execute(&wsv, account_id, wat)?; + + Ok(()) + } } diff --git a/core/src/wasm.rs b/core/src/wasm.rs deleted file mode 100644 index b37bcfba057..00000000000 --- a/core/src/wasm.rs +++ /dev/null @@ -1,381 +0,0 @@ -use eyre::WrapErr; -use iroha_data_model::prelude::*; -use iroha_logger::prelude::*; -use parity_scale_codec::{Decode, Encode}; -use wasmtime::{Caller, Config, Engine, Linker, Module, Store, Trap, TypedFunc}; - -use crate::{ - smartcontracts::{Execute, ValidQuery}, - wsv::{WorldStateView, WorldTrait}, -}; - -const WASM_ALLOC_FN: &str = "alloc"; -const WASM_MEMORY_NAME: &str = "memory"; -const WASM_MAIN_FN_NAME: &str = "main"; -const EXECUTE_ISI_FN_NAME: &str = "execute_isi"; -const EXECUTE_QUERY_FN_NAME: &str = "execute_query"; - -/// `WebAssembly` execution error type -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Runtime initialization failure")] - Initialization(#[source] anyhow::Error), - #[error("Module instantiation failure")] - Instantiation(#[source] anyhow::Error), - #[error("Named export not found")] - ExportNotFound(#[source] anyhow::Error), - #[error("Fn call failed")] - FnCall(#[source] Trap), - /// Some other error happened - #[error("Some other error happened")] - Other(#[source] eyre::Error), -} - -struct State<'a, W: WorldTrait> { - wsv: &'a WorldStateView, - account_id: AccountId, - - /// Number of instructions in the smartcontract - instruction_count: u64, -} - -impl<'a, W: WorldTrait> State<'a, W> { - fn new(wsv: &'a WorldStateView, account_id: AccountId) -> Self { - Self { - wsv, - account_id, - instruction_count: 0, - } - } -} - -/// `WebAssembly` virtual machine -pub struct Runtime<'a, W: WorldTrait> { - engine: Engine, - linker: Linker>, -} - -impl<'a, W: WorldTrait> Runtime<'a, W> { - /// Every WASM instruction costs approximately 1 unit of fuel. See - /// [`wasmtime` reference](https://docs.rs/wasmtime/0.29.0/wasmtime/struct.Store.html#method.add_fuel) - const FUEL_LIMIT: u64 = 10_000; - - fn create_config() -> Config { - let mut config = Config::new(); - config.consume_fuel(true); - //config.cache_config_load_default(); - config - } - - fn create_engine(config: &Config) -> Result { - Engine::new(config).map_err(Error::Initialization) - } - - fn create_linker(engine: &Engine) -> Result>, Error> { - let mut linker = Linker::new(engine); - - linker - .func_wrap( - "iroha", - EXECUTE_ISI_FN_NAME, - |mut caller: Caller>, offset: u32, len: u32| { - let memory = Self::get_memory(&mut caller)?; - - // Accessing memory as a byte slice to avoid the use of unsafe - let isi_mem_range = offset as usize..(offset + len) as usize; - let mut isi_bytes = &memory.data(&caller)[isi_mem_range]; - let instruction = Instruction::decode(&mut isi_bytes) - .map_err(|error| Trap::new(error.to_string()))?; - - instruction - .execute(caller.data().account_id.clone(), caller.data().wsv) - .map_err(|error| Trap::new(error.to_string()))?; - - caller.data_mut().instruction_count += 1; - Ok(()) - }, - ) - .map_err(Error::Initialization)?; - - linker - .func_wrap( - "iroha", - EXECUTE_QUERY_FN_NAME, - |mut caller: Caller>, offset: u32, len: u32| { - let alloc_fn = Self::get_alloc_fn(&mut caller)?; - let memory = Self::get_memory(&mut caller)?; - - // Accessing memory as a byte slice to avoid the use of unsafe - let query_mem_range = offset as usize..(offset + len) as usize; - let mut query_bytes = &memory.data(&caller)[query_mem_range]; - let query = QueryBox::decode(&mut query_bytes) - .map_err(|error| Trap::new(error.to_string()))?; - - let res_bytes = query - .execute(caller.data().wsv) - .map_err(|e| Trap::new(e.to_string()))? - .encode(); - - let res_bytes_len: u32 = { - let res_bytes_len: Result = res_bytes.len().try_into(); - res_bytes_len.map_err(|error| Trap::new(error.to_string()))? - }; - - let res_offset = { - let res_offset = alloc_fn - .call(&mut caller, res_bytes_len) - .map_err(|e| Trap::new(e.to_string()))?; - - let res_mem_range = - res_offset as usize..res_offset as usize + res_bytes.len(); - memory.data_mut(&mut caller)[res_mem_range].copy_from_slice(&res_bytes[..]); - - res_offset - }; - - Ok((res_offset, res_bytes_len)) - }, - ) - .map_err(Error::Initialization)?; - - Ok(linker) - } - - fn get_alloc_fn(caller: &mut Caller>) -> Result, 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::(caller) - .map_err(|_error| Trap::new(format!("{}: unexpected declaration", WASM_ALLOC_FN))) - } - - fn get_memory(caller: &mut Caller>) -> Result { - caller - .get_export(WASM_MEMORY_NAME) - .ok_or_else(|| Trap::new(format!("{}: export not found", WASM_MEMORY_NAME)))? - .into_memory() - .ok_or_else(|| Trap::new(format!("{}: not a memory", WASM_MEMORY_NAME))) - } - - /// `Runtime` constructor - /// - /// # Errors - /// - /// If unable to construct runtime - pub fn new() -> Result { - let config = Self::create_config(); - let engine = Self::create_engine(&config)?; - let linker = Self::create_linker(&engine)?; - - Ok(Self { engine, linker }) - } - - /// Executes the given wasm smartcontract - /// - /// # Errors - /// - /// If unable to construct wasm module or instance of wasm module, if unable to add fuel limit, - /// if unable to find expected exports(main, memory, allocator) or if the execution of the - /// smartcontract fails - pub fn execute( - &mut self, - wsv: &WorldStateView, - account_id: AccountId, - bytes: impl AsRef<[u8]>, - ) -> Result<(), Error> { - let account_bytes = account_id.encode(); - - let module = Module::new(&self.engine, bytes).map_err(Error::Instantiation)?; - let mut store = Store::new(&self.engine, State::new(wsv, account_id)); - store - .add_fuel(Self::FUEL_LIMIT) - .map_err(Error::Instantiation)?; - - let instance = self - .linker - .instantiate(&mut store, &module) - .map_err(Error::Instantiation)?; - let alloc_fn = instance - .get_typed_func::(&mut store, WASM_ALLOC_FN) - .map_err(Error::ExportNotFound)?; - - let memory = instance - .get_memory(&mut store, WASM_MEMORY_NAME) - .ok_or_else(|| { - Error::ExportNotFound(anyhow::Error::msg(format!( - "{}: export not found or not a memory", - WASM_MEMORY_NAME - ))) - })?; - - let account_bytes_len = account_bytes - .len() - .try_into() - .wrap_err("Scale encoded account ID has size larger than u32::MAX") - .map_err(Error::Other)?; - - let account_offset = { - let acc_offset = alloc_fn - .call(&mut store, account_bytes_len) - .map_err(Error::FnCall)?; - - let acc_mem_range = acc_offset as usize..acc_offset as usize + account_bytes.len(); - memory.data_mut(&mut store)[acc_mem_range].copy_from_slice(&account_bytes[..]); - - acc_offset - }; - - let main = instance - .get_typed_func::<(u32, u32), (), _>(&mut store, WASM_MAIN_FN_NAME) - .map_err(Error::ExportNotFound)?; - - main.call(&mut store, (account_offset, account_bytes_len)) - .map_err(Error::FnCall)?; - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - #![allow(clippy::restriction)] - - use iroha_crypto::KeyPair; - use iroha_data_model::{domain::DomainsMap, peer::PeersIds}; - - use super::*; - use crate::World; - - fn world_with_test_account(account_id: AccountId) -> World { - let domain_id = account_id.domain_id.clone(); - let public_key = KeyPair::generate().unwrap().public_key; - let account = Account::with_signatory(account_id, public_key); - let domain = Domain::with_accounts(domain_id.name.as_ref(), std::iter::once(account)); - - let domains = DomainsMap::new(); - domains.insert(domain_id, domain); - World::with(domains, PeersIds::new()) - } - - fn memory_and_alloc(isi_hex: &str) -> String { - format!( - r#" - ;; Embed ISI into WASM binary memory - (memory (export "{memory_name}") 1) - (data (i32.const 0) "{isi_hex}") - - ;; Variable which tracks total allocated size - (global $mem_size (mut i32) i32.const {isi_len}) - - ;; Export mock allocator to host. This allocator never frees! - (func (export "{alloc_fn_name}") (param $size i32) (result i32) - global.get $mem_size - - (global.set $mem_size - (i32.add (global.get $mem_size) (local.get $size)) - ) - ) - "#, - memory_name = WASM_MEMORY_NAME, - alloc_fn_name = WASM_ALLOC_FN, - isi_len = isi_hex.len() / 3, - isi_hex = isi_hex, - ) - } - - fn encode_hex(isi: T) -> String { - let isi_bytes = isi.encode(); - - let mut isi_hex = String::with_capacity(3 * isi_bytes.len()); - for (i, c) in hex::encode(isi_bytes).chars().enumerate() { - if i % 2 == 0 { - isi_hex.push('\\'); - } - - isi_hex.push(c); - } - - isi_hex - } - - #[test] - fn execute_instruction_exported() -> Result<(), Error> { - let account_id = AccountId::test("alice", "wonderland"); - let wsv = WorldStateView::new(world_with_test_account(account_id.clone())); - - let isi_hex = { - let new_account_id = AccountId::test("mad_hatter", "wonderland"); - let register_isi = RegisterBox::new(NewAccount::new(new_account_id)); - encode_hex(Instruction::Register(register_isi)) - }; - - let wat = format!( - r#" - (module - ;; Import host function to execute - (import "iroha" "{execute_fn_name}" - (func $exec_fn (param i32 i32)) - ) - - {memory_and_alloc} - - ;; Function which starts the smartcontract execution - (func (export "{main_fn_name}") (param i32 i32) - (call $exec_fn (i32.const 0) (i32.const {isi_len})) - ) - ) - "#, - main_fn_name = WASM_MAIN_FN_NAME, - execute_fn_name = EXECUTE_ISI_FN_NAME, - memory_and_alloc = memory_and_alloc(&isi_hex), - isi_len = isi_hex.len() / 3, - ); - let mut runtime = Runtime::new()?; - runtime.execute(&wsv, account_id, wat)?; - - Ok(()) - } - - #[test] - fn execute_query_exported() -> Result<(), Error> { - let account_id = AccountId::test("alice", "wonderland"); - let wsv = WorldStateView::new(world_with_test_account(account_id.clone())); - - let query_hex = { - let find_acc_query = FindAccountById::new(account_id.clone()); - encode_hex(QueryBox::FindAccountById(find_acc_query)) - }; - - let wat = format!( - r#" - (module - ;; Import host function to execute - (import "iroha" "{execute_fn_name}" - (func $exec_fn (param i32 i32) (result i32 i32)) - ) - - {memory_and_alloc} - - ;; Function which starts the smartcontract execution - (func (export "{main_fn_name}") (param i32 i32) - (call $exec_fn (i32.const 0) (i32.const {isi_len})) - - ;; No use of return values - drop drop - ) - ) - "#, - main_fn_name = WASM_MAIN_FN_NAME, - execute_fn_name = EXECUTE_QUERY_FN_NAME, - memory_and_alloc = memory_and_alloc(&query_hex), - isi_len = query_hex.len() / 3, - ); - - let mut runtime = Runtime::new()?; - runtime.execute(&wsv, account_id, wat)?; - - Ok(()) - } -} diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 46f72fc7b39..1e48ae30d28 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -8,7 +8,12 @@ extern crate alloc; #[cfg(not(feature = "std"))] -use alloc::{boxed::Box, fmt, format, string::String, vec::Vec}; +use alloc::{ + boxed::Box, + fmt, format, + string::{String, ToString}, + vec::Vec, +}; use core::{fmt::Debug, ops::RangeInclusive, str::FromStr}; #[cfg(feature = "std")] use std::fmt; @@ -474,23 +479,24 @@ impl> From> for Value { } } -#[cfg(feature = "std")] impl TryFrom for Vec where Value: TryInto, - eyre::Error: From<>::Error>, + >::Error: ToString, { - type Error = eyre::Error; + // TODO: Use concrete error type + type Error = String; fn try_from(value: Value) -> Result { if let Value::Vec(vec) = value { - return Ok(vec + return vec .into_iter() .map(TryInto::try_into) - .collect::, _>>()?); + .collect::, _>>() + .map_err(|e| e.to_string()); } - Err(eyre::eyre!("Expected vector, but found something else")) + Err(String::from("Expected vector, but found something else")) } } @@ -544,7 +550,7 @@ pub mod role { //! Structures, traits and impls related to `Role`s. #[cfg(not(feature = "std"))] - use alloc::{boxed::Box, collections::btree_set, fmt, string::String}; + use alloc::{boxed::Box, collections::btree_set, fmt, format, string::String}; #[cfg(feature = "std")] use std::{collections::btree_set, fmt}; @@ -749,6 +755,7 @@ pub mod account { use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; + pub use Id as AccountId; #[cfg(feature = "roles")] use crate::role::Id as RoleId; @@ -1106,7 +1113,7 @@ pub mod account { /// The prelude re-exports most commonly used traits, structs and macros from this crate. pub mod prelude { - pub use super::{Account, Id as AccountId, NewAccount, SignatureCheckCondition}; + pub use super::{Account, AccountId, NewAccount, SignatureCheckCondition}; } } @@ -1115,7 +1122,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}, @@ -1648,7 +1655,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, fmt, string::String, vec::Vec}; + use alloc::{collections::btree_map, fmt, format, string::String, vec::Vec}; use core::{cmp::Ordering, str::FromStr}; #[cfg(feature = "std")] use std::{collections::btree_map, fmt}; @@ -1838,7 +1845,7 @@ pub mod peer { //! This module contains [`Peer`] structure and related implementations and traits implementations. #[cfg(not(feature = "std"))] - use alloc::{fmt, string::String, vec::Vec}; + use alloc::{fmt, format, string::String, vec::Vec}; use core::hash::Hash; #[cfg(feature = "std")] use std::fmt; diff --git a/data_model/src/query.rs b/data_model/src/query.rs index 07234dbfe3c..9921c32b470 100644 --- a/data_model/src/query.rs +++ b/data_model/src/query.rs @@ -145,7 +145,7 @@ declare_versioned_with_scale!(VersionedQueryResult 1..2, Debug, Clone, iroha_mac /// Sized container for all possible Query results. #[version_with_scale(n = 1, versioned = "VersionedQueryResult")] -#[derive(Debug, Clone, Decode, Encode, Deserialize, Serialize, IntoSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] pub struct QueryResult(pub Value); #[cfg(all(feature = "std", feature = "warp"))] diff --git a/wasm_test/Cargo.toml b/wasm/Cargo.toml similarity index 60% rename from wasm_test/Cargo.toml rename to wasm/Cargo.toml index e452cadfa01..9e6f993be35 100644 --- a/wasm_test/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,15 +1,23 @@ [package] -name = "wasm_test" +name = "iroha_wasm" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -crate-type = ['cdylib'] +[features] +default = ["panic_handler"] +panic_handler = [] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" [dependencies] iroha_data_model = { version = "=2.0.0-pre.1", path = "../data_model", default-features = false } -iroha_wasm = { path = "../wasm" } +iroha_wasm_derive = { path = "derive" } -parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] } +parity-scale-codec = { version = "2.3.1", default-features = false } +wee_alloc = "0.4.5" diff --git a/wasm/derive/Cargo.toml b/wasm/derive/Cargo.toml new file mode 100644 index 00000000000..6af4f73751b --- /dev/null +++ b/wasm/derive/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "iroha_wasm_derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +syn = {version = "1", default-features = false } +quote = "1.0" +proc-macro-error = "1.0" + +[dev-dependencies] +iroha_wasm = { path = "../" } + +trybuild = "1.0.53" diff --git a/wasm/derive/src/lib.rs b/wasm/derive/src/lib.rs new file mode 100644 index 00000000000..283ed15c7af --- /dev/null +++ b/wasm/derive/src/lib.rs @@ -0,0 +1,70 @@ +//! Exposes macros which facilitate writing smartcontracts + +#![allow(clippy::str_to_string)] + +use proc_macro::TokenStream; +use proc_macro_error::{abort, proc_macro_error}; +use quote::quote; +use syn::{parse_macro_input, ItemFn, Path, ReturnType, Signature, Type}; + +/// Used to annotate user-defined function which starts the execution of smartcontract +#[proc_macro_error] +#[proc_macro_attribute] +pub fn iroha_wasm(_: TokenStream, item: TokenStream) -> TokenStream { + let ItemFn { + attrs, + vis, + sig, + block, + }: ItemFn = parse_macro_input!(item as ItemFn); + + verify_function_signature(&sig); + let fn_name = &sig.ident; + + quote! { + #[no_mangle] + unsafe extern "C" fn _iroha_wasm_main(ptr: u32, len: u32) { + #fn_name(iroha_wasm::_decode_from_raw::(ptr, len)) + } + + #(#attrs)* + #vis #sig + #block + } + .into() +} + +fn verify_function_signature(sig: &Signature) -> bool { + if ReturnType::Default != sig.output { + abort!(sig.output, "Exported function must not have a return type"); + } + + if sig.inputs.len() != 1 { + abort!( + sig.inputs, + "Exported function must have exactly 1 input argument of type `AccountId`" + ); + } + + if let Some(syn::FnArg::Typed(pat)) = sig.inputs.iter().next() { + if let syn::Type::Reference(ty) = &*pat.ty { + return type_is_account_id(&ty.elem); + } + } + + false +} + +fn type_is_account_id(account_id_ty: &Type) -> bool { + const ACCOUNT_ID_IDENT: &str = "AccountId"; + + if let Type::Path(path) = account_id_ty { + let Path { segments, .. } = &path.path; + + if let Some(type_name) = segments.iter().last().map(|ty| &ty.ident) { + return *type_name == ACCOUNT_ID_IDENT; + } + } + + false +} diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs new file mode 100644 index 00000000000..df12fbaa9eb --- /dev/null +++ b/wasm/src/lib.rs @@ -0,0 +1,191 @@ +#![feature(alloc_error_handler)] +#![cfg_attr(not(test), no_std)] +//#![allow(unsafe_code)] + +#[cfg(not(test))] +extern crate alloc; + +#[cfg(not(test))] +use alloc::{boxed::Box, format, vec::Vec}; + +use data_model::prelude::*; +pub use iroha_data_model as data_model; +pub use iroha_wasm_derive::iroha_wasm; +use parity_scale_codec::{Decode, Encode}; + +#[cfg(not(test))] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +#[cfg(not(test))] +#[no_mangle] +#[panic_handler] +fn panic(_info: &::core::panic::PanicInfo) -> ! { + // Need to provide a tiny `panic` implementation for `#![no_std]`. + // This translates into an `unreachable` instruction that will + // raise a `trap` the WebAssembly execution if we panic at runtime. + unreachable!("Program should have aborted") +} + +#[cfg(not(test))] +#[no_mangle] +#[alloc_error_handler] +fn oom(layout: ::core::alloc::Layout) -> ! { + panic!("Allocation({} bytes) failed", layout.size()) +} + +#[no_mangle] +extern "C" fn _iroha_wasm_alloc(len: u32) -> u32 { + core::mem::ManuallyDrop::new(Vec::::with_capacity(len as usize)).as_mut_ptr() as u32 +} + +/// Host exports +mod host { + /// Helper struct which guarantees to be FFI safe since tuple is not + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub(super) struct WasmQueryResult(pub u32, pub u32); + + #[link(wasm_import_module = "iroha")] + extern "C" { + /// Executes encoded query by providing offset and length + /// into WebAssembly's linear memory where query is stored + /// + /// # Warning + /// + /// This function doesn't take ownership of the provided allocation + /// but it does transfer ownership of the result to the caller + #[cfg(not(test))] + pub(super) fn execute_query(ptr: u32, len: u32) -> WasmQueryResult; + + /// Executes encoded instruction by providing offset and length + /// into WebAssembly's linear memory where instruction is stored + /// + /// # Warning + /// + /// This function doesn't take ownership of the provided allocation + /// but it does transfer ownership of the result to the caller + #[cfg(not(test))] + pub(super) fn execute_instruction(ptr: u32, len: u32); + } +} + +/// Decode the object from given pointer and length +/// +/// # Warning +/// +/// This method takes ownership of the given pointer +pub unsafe fn _decode_from_raw(ptr: u32, len: u32) -> T { + let bytes = Box::from_raw(core::slice::from_raw_parts_mut(ptr as *mut _, len as usize)); + + T::decode(&mut &bytes[..]).expect( + format!( + "Decoding of {} failed. This is a bug", + core::any::type_name::() + ) + .as_str(), + ) +} + +/// Encode the given object and call the given function with the pointer and length of the allocation +/// +/// # Warning +/// +/// Ownership of the returned allocation is transfered to the caller +/// +/// # Safety +/// +/// The given function must not take ownership of the pointer argument +unsafe fn encode_and_execute(obj: T, fun: unsafe extern "C" fn(u32, u32) -> O) -> O { + // NOTE: It's imperative that encoded object is stored on the heap + // because heap corresponds to linear memory when compiled to wasm + let bytes = obj.encode(); + + // NOTE: This is valid as long as it's compiled for 32-bit architecture + #[allow(clippy::cast_possible_truncation)] + let ptr = bytes.as_ptr() as u32; + #[allow(clippy::cast_possible_truncation)] + let len = bytes.len() as u32; + + fun(ptr, len) +} + +/// Executes the given query on the host environment +pub fn execute_query(query: QueryBox) -> QueryResult { + #[cfg(not(test))] + use host::execute_query as host_execute_query; + #[cfg(test)] + use tests::_iroha_wasm_execute_query_mock as host_execute_query; + + #[allow(clippy::expect_used)] + unsafe { + let host::WasmQueryResult(res_ptr, res_len) = encode_and_execute(query, host_execute_query); + _decode_from_raw(res_ptr, res_len) + } +} + +/// Execute the given instruction on the host environment +pub fn execute_instruction(instruction: Instruction) { + #[cfg(not(test))] + use host::execute_instruction as host_execute_instruction; + #[cfg(test)] + use tests::_iroha_wasm_execute_instruction_mock as host_execute_instruction; + + unsafe { encode_and_execute(instruction, host_execute_instruction) }; +} + +#[cfg(test)] +mod tests { + #![allow(clippy::restriction)] + #![allow(clippy::pedantic)] + + use core::{mem::ManuallyDrop, slice}; + + use super::*; + + fn get_test_instruction() -> Instruction { + let new_account_id = AccountId::test("mad_hatter", "wonderland"); + let register_isi = RegisterBox::new(NewAccount::new(new_account_id)); + + Instruction::Register(register_isi) + } + fn get_test_query() -> QueryBox { + let account_id = AccountId::test("alice", "wonderland"); + FindAccountById::new(account_id).into() + } + fn get_query_result() -> QueryResult { + QueryResult(Value::U32(1234)) + } + + #[no_mangle] + pub(super) unsafe extern "C" fn _iroha_wasm_execute_instruction_mock(ptr: u32, len: u32) { + let bytes = slice::from_raw_parts(ptr as *const _, len as usize); + let instruction = Instruction::decode(&mut &*bytes); + assert_eq!(get_test_instruction(), instruction.unwrap()); + } + + #[no_mangle] + pub(super) unsafe extern "C" fn _iroha_wasm_execute_query_mock( + ptr: u32, + len: u32, + ) -> host::WasmQueryResult { + let bytes = slice::from_raw_parts(ptr as *const _, len as usize); + let query = QueryBox::decode(&mut &*bytes).unwrap(); + assert_eq!(query, get_test_query()); + + let query_result = get_query_result(); + let bytes = ManuallyDrop::new(query_result.encode().into_boxed_slice()); + + host::WasmQueryResult(bytes.as_ptr() as u32, bytes.len() as u32) + } + + #[test] + fn execute_instruction_test() { + execute_instruction(get_test_instruction()) + } + + #[test] + fn execute_query_test() { + assert_eq!(execute_query(get_test_query()), get_query_result()); + } +} diff --git a/wasm_test/Cargo.lock b/wasm_test/Cargo.lock deleted file mode 100644 index 449a160a511..00000000000 --- a/wasm_test/Cargo.lock +++ /dev/null @@ -1,909 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byte-slice-cast" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" -dependencies = [ - "libc", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "fixnum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83b4df6079ff2f20dfd4c4a13a5c326d985ae35e289df51ff95e71e76b7ada5" -dependencies = [ - "derive_more", - "parity-scale-codec", - "serde", - "static_assertions", - "typenum", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" - -[[package]] -name = "futures-sink" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" - -[[package]] -name = "futures-task" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" - -[[package]] -name = "futures-util" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" -dependencies = [ - "futures-core", - "futures-sink", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "h2" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "headers" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4eb0471fcb85846d8b0690695ef354f9afb11cb03cac2e1d7c9253351afb0" -dependencies = [ - "base64", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha-1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "http" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.1", -] - -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 0.4.8", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "indexmap" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "iroha_crypto" -version = "2.0.0-pre.1" -dependencies = [ - "derive_more", - "hex", - "iroha_schema", - "parity-scale-codec", - "serde", -] - -[[package]] -name = "iroha_data_model" -version = "2.0.0-pre.1" -dependencies = [ - "fixnum", - "iroha_crypto", - "iroha_macro", - "iroha_schema", - "iroha_version", - "parity-scale-codec", - "serde", - "serde_json", -] - -[[package]] -name = "iroha_derive" -version = "2.0.0-pre.1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "iroha_macro" -version = "2.0.0-pre.1" -dependencies = [ - "iroha_derive", -] - -[[package]] -name = "iroha_schema" -version = "2.0.0-pre.1" -dependencies = [ - "fixnum", - "iroha_schema_derive", - "serde", -] - -[[package]] -name = "iroha_schema_derive" -version = "2.0.0-pre.1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "iroha_version" -version = "2.0.0-pre.1" -dependencies = [ - "iroha_macro", - "iroha_version_derive", - "parity-scale-codec", - "serde", - "serde_json", - "warp", -] - -[[package]] -name = "iroha_version_derive" -version = "2.0.0-pre.1" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "iroha_wasm" -version = "0.1.0" -dependencies = [ - "iroha_data_model", - "iroha_wasm_derive", - "parity-scale-codec", -] - -[[package]] -name = "iroha_wasm_derive" -version = "0.1.0" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.112" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mime_guess" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "parity-scale-codec" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" -dependencies = [ - "arrayvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "proc-macro-crate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - -[[package]] -name = "serde" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" -dependencies = [ - "itoa 1.0.1", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "slab" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" - -[[package]] -name = "socket2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "syn" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" -dependencies = [ - "bytes", - "libc", - "memchr", - "mio", - "pin-project-lite", - "winapi", -] - -[[package]] -name = "tokio-stream" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "warp" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-stream", - "tokio-util", - "tower-service", - "tracing", -] - -[[package]] -name = "wasm_test" -version = "0.1.0" -dependencies = [ - "iroha_data_model", - "iroha_wasm", - "parity-scale-codec", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"