diff --git a/Cargo.lock b/Cargo.lock index e06448d06dc01..c139749441f71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,6 +470,23 @@ dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hex-literal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex-literal-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "httparse" version = "1.2.3" @@ -768,6 +785,7 @@ dependencies = [ name = "native-runtime" version = "0.1.0" dependencies = [ + "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "runtime-std 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -968,7 +986,6 @@ dependencies = [ "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "native-runtime 0.1.0", "parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", @@ -978,6 +995,7 @@ dependencies = [ "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", + "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1061,6 +1079,7 @@ version = "0.1.0" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", @@ -1086,6 +1105,19 @@ dependencies = [ "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro-hack" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "0.3.15" @@ -1203,6 +1235,7 @@ dependencies = [ "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", "polkadot-state-machine 0.1.0", + "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1705,6 +1738,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a" "checksum hashdb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d97be07c358c5b461268b4ce60304024c5fa5acfd4bd8cd743639f0252003cf5" "checksum heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "54fab2624374e5137ae4df13bf32b0b269cb804df42d13a51221bbd431d1a237" +"checksum hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd546ef520ab3745f1aae5f2cdc6de9e6498e94d1ab138b9eb3ddfbf335847fb" +"checksum hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea76da4c7f1a54d01d54985566d3fdd960b2bbd7b970da024821c883c2d9631" "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" "checksum hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4959ca95f55df4265bff2ad63066147255e6fa733682cf6d1cb5eaff6e53324b" @@ -1753,6 +1788,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum plain_hasher 0.1.0 (git+https://github.com/paritytech/parity.git)" = "" "checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995" "checksum pretty_assertions 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94b6bbc8a323d89a019c4cdde21850522fb8405e97add70827177fc2f86c1495" +"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" +"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" "checksum rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b614fe08b6665cb9a231d07ac1364b0ef3cb3698f1239ee0c4c3a88a524f54c8" diff --git a/executor/Cargo.toml b/executor/Cargo.toml index 5329eb7023098..41fdef97057f6 100644 --- a/executor/Cargo.toml +++ b/executor/Cargo.toml @@ -15,7 +15,7 @@ byteorder = "1.1" rustc-hex = "1.0.0" native-runtime = { path = "../native-runtime", version = "0.1" } runtime-std = { path = "../native-runtime/std", version = "0.1" } -libc = { version = "0.2.33" } +triehash = "0.1.0" [dev-dependencies] assert_matches = "1.1" diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 9935f8aec81d2..e4ed7f95b0e8a 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -36,7 +36,7 @@ extern crate byteorder; extern crate rustc_hex; extern crate native_runtime; extern crate runtime_std; -extern crate libc; +extern crate triehash; #[macro_use] extern crate error_chain; diff --git a/executor/src/native_executor.rs b/executor/src/native_executor.rs index b8c90f2d9a760..6cf7ac6c313c6 100644 --- a/executor/src/native_executor.rs +++ b/executor/src/native_executor.rs @@ -41,8 +41,9 @@ impl CodeExecutor for NativeExecutor { #[cfg(test)] mod tests { use super::*; + use runtime_std::TestExternalities; use native_runtime::codec::KeyedVec; - use native_runtime::support::{TestExternalities, one, two, StaticHexInto}; + use native_runtime::support::{one, two, StaticHexInto}; use native_runtime::runtime::staking::balance; use primitives::twox_128; diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 439defd449cd7..e1e4f0b6d9273 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -29,6 +29,7 @@ use wasm_utils::{MemoryInstance, UserDefinedElements, AddModuleWithoutFullDependentInstance}; use primitives::{ed25519, blake2_256, twox_128, twox_256}; use primitives::hexdisplay::HexDisplay; +use triehash::ordered_trie_root; struct Heap { end: u32, @@ -66,15 +67,26 @@ impl<'e, E: Externalities> FunctionExecutor<'e, E> { } trait WritePrimitive { - fn write_primitive(&self, offset: u32, t: T); + fn write_primitive(&self, offset: u32, t: T) -> ::std::result::Result<(), DummyUserError>; } impl WritePrimitive for MemoryInstance { - fn write_primitive(&self, offset: u32, t: u32) { + fn write_primitive(&self, offset: u32, t: u32) -> ::std::result::Result<(), DummyUserError> { use byteorder::{LittleEndian, ByteOrder}; let mut r = [0u8; 4]; LittleEndian::write_u32(&mut r, t); - let _ = self.set(offset, &r); + self.set(offset, &r).map_err(|_| DummyUserError) + } +} + +trait ReadPrimitive { + fn read_primitive(&self, offset: u32) -> ::std::result::Result; +} + +impl ReadPrimitive for MemoryInstance { + fn read_primitive(&self, offset: u32) -> ::std::result::Result { + use byteorder::{LittleEndian, ByteOrder}; + Ok(LittleEndian::read_u32(&self.get(offset, 4).map_err(|_| DummyUserError)?)) } } @@ -95,29 +107,29 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, println!("Runtime: {}", number); }, ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => { - if let (Ok(sl1), Ok(sl2)) - = (this.memory.get(s1, n as usize), this.memory.get(s2, n as usize)) { - match sl1.cmp(&sl2) { - Ordering::Greater => 1, - Ordering::Less => -1, - Ordering::Equal => 0, - } - } else { - return Err(DummyUserError.into()); + let sl1 = this.memory.get(s1, n as usize).map_err(|_| DummyUserError)?; + let sl2 = this.memory.get(s2, n as usize).map_err(|_| DummyUserError)?; + match sl1.cmp(&sl2) { + Ordering::Greater => 1, + Ordering::Less => -1, + Ordering::Equal => 0, } }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - let _ = this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize); + this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize) + .map_err(|_| DummyUserError)?; println!("memcpy {} from {}, {} bytes", dest, src, count); dest }, ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - let _ = this.memory.copy(src as usize, dest as usize, count as usize); + this.memory.copy(src as usize, dest as usize, count as usize) + .map_err(|_| DummyUserError)?; println!("memmove {} from {}, {} bytes", dest, src, count); dest }, ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { - let _ = this.memory.clear(dest as usize, val as u8, count as usize); + this.memory.clear(dest as usize, val as u8, count as usize) + .map_err(|_| DummyUserError)?; println!("memset {} with {}, {} bytes", dest, val, count); dest }, @@ -136,90 +148,80 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, } }, ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { - let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { - if let Ok(value) = this.ext.storage(&key) { - let offset = this.heap.allocate(value.len() as u32) as u32; - let _ = this.memory.set(offset, &value); - (offset, value.len() as u32) - } else { (0, 0) } - } else { (0, 0) }; - - this.memory.write_primitive(written_out, written); - offset as u32 + let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; + let value = this.ext.storage(&key).map_err(|_| DummyUserError)?; + + let offset = this.heap.allocate(value.len() as u32) as u32; + this.memory.set(offset, &value).map_err(|_| DummyUserError)?; + + this.memory.write_primitive(written_out, value.len() as u32)?; + offset }, ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => { - if let Ok(key) = this.memory.get(key_data, key_len as usize) { - if let Ok(value) = this.ext.storage(&key) { - let value = &value[value_offset as usize..]; - let written = ::std::cmp::min(value_len as usize, value.len()); - let _ = this.memory.set(value_data, &value[..written]); - written as u32 - } else { 0 } - } else { 0 } + let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; + let value = this.ext.storage(&key).map_err(|_| DummyUserError)?; + let value = &value[value_offset as usize..]; + let written = ::std::cmp::min(value_len as usize, value.len()); + this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?; + written as u32 + }, + ext_storage_root(result: *mut u8) => { + let r = this.ext.storage_root(); + this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?; + }, + ext_enumerated_trie_root(values_data: *const u8, values_len: u32, lens_data: *const u32, lens_len: u32, result: *mut u8) => { + let values = (0..lens_len) + .map(|i| this.memory.read_primitive(lens_data + i * 4)) + .collect::<::std::result::Result, DummyUserError>>()? + .into_iter() + .scan(0u32, |acc, v| { let o = *acc; *acc += v; Some((o, v)) }) + .map(|(offset, len)| + this.memory.get(values_data + offset, len as usize) + .map_err(|_| DummyUserError) + ) + .collect::<::std::result::Result, DummyUserError>>()?; + let r = ordered_trie_root(values.into_iter()); + this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?; }, ext_chain_id() -> u64 => { this.ext.chain_id() }, ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { - let maybe_value = if len == 0 { - Ok(vec![]) - } else { - this.memory.get(data, len as usize) - }; - let result = if let Ok(value) = maybe_value { - twox_128(&value) + let result = if len == 0 { + twox_128(&[0u8; 0]) } else { - [0; 16] + twox_128(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) }; - let _ = this.memory.set(out, &result); + this.memory.set(out, &result).map_err(|_| DummyUserError)?; }, ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { - let maybe_value = if len == 0 { - Ok(vec![]) + let result = if len == 0 { + twox_256(&[0u8; 0]) } else { - this.memory.get(data, len as usize) + twox_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) }; - let result = if let Ok(value) = maybe_value { - twox_256(&value) - } else { - [0; 32] - }; - let _ = this.memory.set(out, &result); + this.memory.set(out, &result).map_err(|_| DummyUserError)?; }, ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { - let maybe_value = if len == 0 { - Ok(vec![]) - } else { - this.memory.get(data, len as usize) - }; - let result = if let Ok(value) = maybe_value { - blake2_256(&value) + let result = if len == 0 { + blake2_256(&[0u8; 0]) } else { - [0; 32] + blake2_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?) }; - let _ = this.memory.set(out, &result); + this.memory.set(out, &result).map_err(|_| DummyUserError)?; }, ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { - (||{ - let mut sig = [0u8; 64]; - if let Err(_) = this.memory.get_into(sig_data, &mut sig[..]) { - return 2; - }; - let mut pubkey = [0u8; 32]; - if let Err(_) = this.memory.get_into(pubkey_data, &mut pubkey[..]) { - return 3; - }; - - if let Ok(msg) = this.memory.get(msg_data, msg_len as usize) { - if ed25519::Signature::from(sig).verify(&msg, &ed25519::Public::from(pubkey)) { - 0 - } else { - 5 - } - } else { - 4 - } - })() + let mut sig = [0u8; 64]; + this.memory.get_into(sig_data, &mut sig[..]).map_err(|_| DummyUserError)?; + let mut pubkey = [0u8; 32]; + this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| DummyUserError)?; + let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| DummyUserError)?; + + if ed25519::Signature::from(sig).verify(&msg, &ed25519::Public::from(pubkey)) { + 0 + } else { + 5 + } } => <'e, E: Externalities + 'e> ); @@ -279,8 +281,8 @@ mod tests { use super::*; use rustc_hex::FromHex; use primitives::{blake2_256, twox_128}; - use runtime_std; - use native_runtime::support::{one, two, StaticHexInto, TestExternalities}; + use runtime_std::{self, TestExternalities}; + use native_runtime::support::{one, two, StaticHexInto}; use native_runtime::codec::KeyedVec; use native_runtime::runtime::staking::balance; @@ -380,6 +382,16 @@ mod tests { ); } + #[test] + fn enumerated_trie_root_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + assert_eq!( + WasmExecutor.call(&mut ext, &test_code[..], "test_enumerated_trie_root", &CallData(vec![])).unwrap(), + ordered_trie_root(vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]).0.to_vec() + ); + } + fn tx() -> Vec { "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000".convert() } #[test] diff --git a/native-runtime/Cargo.toml b/native-runtime/Cargo.toml index 25091c2cefb60..5ee1706e9cf08 100644 --- a/native-runtime/Cargo.toml +++ b/native-runtime/Cargo.toml @@ -11,3 +11,4 @@ without-std = [] [dependencies] runtime-std = { path = "./std", version = "0.1" } rustc-hex = "1.0" +hex-literal = "0.1.0" diff --git a/native-runtime/std/Cargo.toml b/native-runtime/std/Cargo.toml index 0976e4b378fe8..a4f665bfcce28 100644 --- a/native-runtime/std/Cargo.toml +++ b/native-runtime/std/Cargo.toml @@ -12,3 +12,4 @@ parking_lot = "0.5" polkadot-state-machine = { path = "../../state_machine" , version = "0.1" } environmental = { path = "../../environmental", version = "0.1.0" } polkadot-primitives = { path = "../../primitives", version = "0.1.0" } +triehash = "0.1.0" diff --git a/native-runtime/std/src/lib.rs b/native-runtime/std/src/lib.rs index fb74093d186ba..e24e9d7bb3a05 100644 --- a/native-runtime/std/src/lib.rs +++ b/native-runtime/std/src/lib.rs @@ -20,6 +20,7 @@ extern crate environmental; extern crate polkadot_state_machine; extern crate polkadot_primitives as primitives; +extern crate triehash; use std::fmt; use primitives::ed25519; @@ -39,7 +40,7 @@ pub mod prelude { pub use std::boxed::Box; } -pub use polkadot_state_machine::{Externalities, ExternalitiesError}; +pub use polkadot_state_machine::{Externalities, ExternalitiesError, TestExternalities}; use primitives::hexdisplay::HexDisplay; // TODO: use the real error, not NoError. @@ -92,6 +93,18 @@ pub fn chain_id() -> u64 { ).unwrap_or(0) } +/// "Commit" all existing operations and get the resultant storage root. +pub fn storage_root() -> [u8; 32] { + ext::with(|ext| + ext.storage_root() + ).unwrap_or([0u8; 32]) +} + +/// "Commit" all existing operations and get the resultant storage root. +pub fn enumerated_trie_root(serialised_values: &[&[u8]]) -> [u8; 32] { + triehash::ordered_trie_root(serialised_values.iter().map(|s| s.to_vec())).0 +} + /// Conduct a Keccak-256 hash of the given data. pub use primitives::{blake2_256, twox_128, twox_256}; @@ -140,23 +153,6 @@ macro_rules! impl_stubs { #[cfg(test)] mod tests { use super::*; - use std::collections::HashMap; - - #[derive(Debug, Default)] - struct TestExternalities { - storage: HashMap, Vec>, - } - impl Externalities for TestExternalities { - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { - Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) - } - - fn set_storage(&mut self, key: Vec, value: Vec) { - self.storage.insert(key, value); - } - - fn chain_id(&self) -> u64 { 42 } - } macro_rules! map { ($( $name:expr => $value:expr ),*) => ( diff --git a/state_machine/Cargo.toml b/state_machine/Cargo.toml index 458bae8e1efe4..65c6feed54cd7 100644 --- a/state_machine/Cargo.toml +++ b/state_machine/Cargo.toml @@ -11,3 +11,4 @@ patricia-trie = "0.1.0" memorydb = "0.1.1" triehash = "0.1" byteorder = "1.1" +hex-literal = "0.1.0" diff --git a/state_machine/src/backend.rs b/state_machine/src/backend.rs index a97bf2342d211..5057339ce943e 100644 --- a/state_machine/src/backend.rs +++ b/state_machine/src/backend.rs @@ -40,6 +40,9 @@ pub trait Backend { /// Commit updates to the backend and get new state. fn commit(&mut self, changes: I) -> Committed where I: IntoIterator; + + /// Get all key/value pairs into a Vec. + fn pairs(&self) -> Vec<(&[u8], &[u8])>; } /// Error impossible. @@ -64,6 +67,18 @@ pub struct InMemory { inner: MemoryState, // keeps all the state in memory. } +#[cfg(test)] +impl InMemory { + /// Create a new instance from a given storage map. + pub fn from(storage: ::std::collections::HashMap, Vec>) -> Self { + InMemory { + inner: MemoryState { + storage + } + } + } +} + impl Backend for InMemory { type Error = Void; @@ -87,6 +102,10 @@ impl Backend for InMemory { storage_tree_root, } } + + fn pairs(&self) -> Vec<(&[u8], &[u8])> { + self.inner.storage.iter().map(|(k, v)| (&k[..], &v[..])).collect() + } } // TODO: DB-based backend diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index 764c51de10df8..1beca17fa6f81 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -17,7 +17,7 @@ //! Conrete externalities implementation. use std::{error, fmt}; - +use triehash::trie_root; use backend::Backend; use {Externalities, ExternalitiesError, OverlayedChanges}; @@ -75,4 +75,15 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> fn chain_id(&self) -> u64 { 42 } + + fn storage_root(&self) -> [u8; 32] { + let mut all_pairs = self.backend.pairs(); + all_pairs.extend( + self.overlay.committed.storage.iter() + .chain(self.overlay.prospective.storage.iter()) + .map(|(k, v)| (&k[..], &v[..])) + ); + + trie_root(all_pairs.into_iter().map(|(k, v)| (k.to_vec(), v.to_vec()))).0 + } } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index a121cf09c79a2..e497a20474b55 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -19,6 +19,8 @@ #![warn(missing_docs)] extern crate polkadot_primitives as primitives; +#[macro_use] +extern crate hex_literal; extern crate hashdb; extern crate memorydb; @@ -35,6 +37,9 @@ use primitives::contract::{CallData}; pub mod backend; mod ext; +mod testing; + +pub use testing::TestExternalities; /// Updates to be committed to the state. pub enum Update { @@ -141,6 +146,9 @@ pub trait Externalities { /// Get the identity of the chain. fn chain_id(&self) -> u64; + /// Get the trie root of the current storage map. + fn storage_root(&self) -> [u8; 32]; + /// Get the current set of authorities from storage. fn authorities(&self) -> Result, ExternalitiesError> { (0..self.storage(b"con:aut:len")?.into_iter() @@ -210,8 +218,9 @@ pub fn execute( #[cfg(test)] mod tests { - use std::collections::HashMap; - use super::{OverlayedChanges, Externalities, ExternalitiesError}; + use super::*; + use super::backend::InMemory; + use super::ext::Ext; #[test] fn overlayed_storage_works() { @@ -238,22 +247,6 @@ mod tests { assert!(overlayed.storage(&key).is_none()); } - #[derive(Debug, Default)] - struct TestExternalities { - storage: HashMap, Vec>, - } - impl Externalities for TestExternalities { - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { - Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) - } - - fn set_storage(&mut self, key: Vec, value: Vec) { - self.storage.insert(key, value); - } - - fn chain_id(&self) -> u64 { 42 } - } - #[test] fn authorities_call_works() { let mut ext = TestExternalities::default(); @@ -273,4 +266,34 @@ mod tests { ext.set_storage(b"con:aut:\x01\0\0\0".to_vec(), b"second".to_vec()); assert_eq!(ext.authorities(), Ok(vec![&b"first"[..], &b"second"[..]])); } + + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + + #[test] + fn overlayed_storage_root_works() { + let mut backend = InMemory::from(map![ + b"doe".to_vec() => b"reindeer".to_vec(), + b"dog".to_vec() => b"puppyXXX".to_vec(), + b"dogglesworth".to_vec() => b"catXXX".to_vec() + ]); + let mut overlay = OverlayedChanges { + committed: MemoryState { storage: map![ + b"dog".to_vec() => b"puppy".to_vec(), + b"dogglesworth".to_vec() => b"catYYY".to_vec() + ], }, + prospective: MemoryState { storage: map![ + b"dogglesworth".to_vec() => b"cat".to_vec() + ], }, + }; + let ext = Ext { + backend: &mut backend, + overlay: &mut overlay, + }; + const ROOT: [u8; 32] = hex!("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3"); + assert_eq!(ext.storage_root(), ROOT); + } } diff --git a/state_machine/src/testing.rs b/state_machine/src/testing.rs new file mode 100644 index 0000000000000..1c9c4dfcf8fa3 --- /dev/null +++ b/state_machine/src/testing.rs @@ -0,0 +1,68 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Test implementation for Externalities. + +use std::collections::HashMap; +use super::{Externalities, ExternalitiesError}; +use triehash::trie_root; + +/// Simple HashMap based Externalities impl. +#[derive(Debug, Default)] +pub struct TestExternalities { + /// The storage. + pub storage: HashMap, Vec>, +} + +impl TestExternalities { + /// Create a new instance with empty storage. + pub fn new() -> Self { + TestExternalities { + storage: HashMap::new(), + } + } +} + +impl Externalities for TestExternalities { + fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + } + + fn set_storage(&mut self, key: Vec, value: Vec) { + self.storage.insert(key, value); + } + + fn chain_id(&self) -> u64 { 42 } + + fn storage_root(&self) -> [u8; 32] { + trie_root(self.storage.clone()).0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn commit_should_work() { + let mut ext = TestExternalities::new(); + ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); + ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); + ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); + const ROOT: [u8; 32] = hex!("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3"); + assert_eq!(ext.storage_root(), ROOT); + } +} diff --git a/wasm-runtime/polkadot/src/lib.rs b/wasm-runtime/polkadot/src/lib.rs index 1eeff3c64950e..677272e93f472 100644 --- a/wasm-runtime/polkadot/src/lib.rs +++ b/wasm-runtime/polkadot/src/lib.rs @@ -25,6 +25,10 @@ extern crate runtime_std; #[cfg(feature = "with-std")] extern crate rustc_hex; +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + pub mod codec; #[macro_use] pub mod support; diff --git a/wasm-runtime/polkadot/src/runtime/governance.rs b/wasm-runtime/polkadot/src/runtime/governance.rs index 4f7f6d8688ee5..49605ce3eff6a 100644 --- a/wasm-runtime/polkadot/src/runtime/governance.rs +++ b/wasm-runtime/polkadot/src/runtime/governance.rs @@ -114,9 +114,9 @@ pub mod internal { #[cfg(test)] mod tests { use super::*; - use runtime_std::{with_externalities, twox_128}; + use runtime_std::{with_externalities, twox_128, TestExternalities}; use codec::{KeyedVec, Joiner}; - use support::{one, two, TestExternalities, with_env}; + use support::{one, two, with_env}; use primitives::{AccountID, InternalFunction}; use runtime::{staking, session}; diff --git a/wasm-runtime/polkadot/src/runtime/session.rs b/wasm-runtime/polkadot/src/runtime/session.rs index 0402816fdc7b1..6cda783beb608 100644 --- a/wasm-runtime/polkadot/src/runtime/session.rs +++ b/wasm-runtime/polkadot/src/runtime/session.rs @@ -132,9 +132,9 @@ mod tests { use super::public::*; use super::privileged::*; use super::internal::*; - use runtime_std::{with_externalities, twox_128}; + use runtime_std::{with_externalities, twox_128, TestExternalities}; use codec::{KeyedVec, Joiner}; - use support::{one, two, TestExternalities, with_env}; + use support::{one, two, with_env}; use primitives::AccountID; use runtime::{consensus, session}; diff --git a/wasm-runtime/polkadot/src/runtime/staking.rs b/wasm-runtime/polkadot/src/runtime/staking.rs index d9bb971647e51..df8d4d632fe5f 100644 --- a/wasm-runtime/polkadot/src/runtime/staking.rs +++ b/wasm-runtime/polkadot/src/runtime/staking.rs @@ -206,9 +206,9 @@ mod tests { use super::public::*; use super::privileged::*; - use runtime_std::{with_externalities, twox_128}; + use runtime_std::{with_externalities, twox_128, TestExternalities}; use codec::{KeyedVec, Joiner}; - use support::{one, two, TestExternalities, with_env}; + use support::{one, two, with_env}; use primitives::AccountID; use runtime::{staking, session}; diff --git a/wasm-runtime/polkadot/src/runtime/system.rs b/wasm-runtime/polkadot/src/runtime/system.rs index a6bf24718e031..8df20f138e7eb 100644 --- a/wasm-runtime/polkadot/src/runtime/system.rs +++ b/wasm-runtime/polkadot/src/runtime/system.rs @@ -18,8 +18,8 @@ //! and depositing logs. use runtime_std::prelude::*; -use runtime_std::{mem, print}; -use codec::KeyedVec; +use runtime_std::{mem, print, storage_root, enumerated_trie_root}; +use codec::{KeyedVec, Slicable}; use support::{Hashable, storage, with_env}; use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder}; use runtime::{staking, session}; @@ -74,13 +74,11 @@ pub mod internal { "Parent hash should be valid." ); - // TODO: check transaction trie root represents the transactions. - // this requires non-trivial changes to the externals API or compiling trie rooting into wasm - // so will wait until a little later. - - // store the header hash in storage. - let header_hash_key = header.number.to_keyed_vec(BLOCK_HASH_AT); - storage::put(&header_hash_key, &header.blake2_256()); + // check transaction trie root represents the transactions. + let txs = block.transactions.iter().map(Slicable::to_vec).collect::>(); + let txs_root = enumerated_trie_root(&txs.iter().map(Vec::as_slice).collect::>()); +// println!("TR: {}", ::support::HexDisplay::from(&txs_root)); + assert!(header.transaction_root == txs_root, "Transaction trie root must be valid."); // execute transactions block.transactions.iter().for_each(execute_transaction); @@ -91,9 +89,14 @@ pub mod internal { // any final checks final_checks(&block); - // TODO: check storage root. - // this requires non-trivial changes to the externals API or compiling trie rooting into wasm - // so will wait until a little later. + + // check storage root. + assert!(header.state_root == storage_root(), "Storage root must match that calculated."); + + // store the header hash in storage; we can't do it before otherwise there would be a + // cyclic dependency. + let header_hash_key = header.number.to_keyed_vec(BLOCK_HASH_AT); + storage::put(&header_hash_key, &header.blake2_256()); } /// Execute a given transaction. @@ -127,10 +130,10 @@ mod tests { use super::*; use super::internal::*; - use runtime_std::{with_externalities, twox_128}; + use runtime_std::{with_externalities, twox_128, TestExternalities}; use codec::{Joiner, KeyedVec, Slicable}; - use support::{StaticHexInto, TestExternalities, HexDisplay, one, two}; - use primitives::{UncheckedTransaction, Transaction, Function}; + use support::{StaticHexInto, HexDisplay, one, two}; + use primitives::{UncheckedTransaction, Transaction, Function, Header, Digest}; use runtime::staking; #[test] @@ -162,4 +165,105 @@ mod tests { assert_eq!(staking::balance(&two), 69); }); } + + fn new_test_ext() -> TestExternalities { + let one = one(); + let two = two(); + let three = [3u8; 32]; + + TestExternalities { storage: map![ + twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].to_vec(), + twox_128(b"gov:apr").to_vec() => vec![].join(&667u32), + twox_128(b"ses:len").to_vec() => vec![].join(&2u64), + twox_128(b"ses:val:len").to_vec() => vec![].join(&3u32), + twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => one.to_vec(), + twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => two.to_vec(), + twox_128(&2u32.to_keyed_vec(b"ses:val:")).to_vec() => three.to_vec(), + twox_128(b"sta:wil:len").to_vec() => vec![].join(&3u32), + twox_128(&0u32.to_keyed_vec(b"sta:wil:")).to_vec() => one.to_vec(), + twox_128(&1u32.to_keyed_vec(b"sta:wil:")).to_vec() => two.to_vec(), + twox_128(&2u32.to_keyed_vec(b"sta:wil:")).to_vec() => three.to_vec(), + twox_128(b"sta:spe").to_vec() => vec![].join(&2u64), + twox_128(b"sta:vac").to_vec() => vec![].join(&3u64), + twox_128(b"sta:era").to_vec() => vec![].join(&0u64), + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ], } + } + + #[test] + fn block_import_works() { + let one = one(); + let two = two(); + + let mut t = new_test_ext(); + + let tx = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 0, + function: Function::StakingTransfer, + input_data: vec![].join(&two).join(&69u64), + }, + signature: "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a".convert(), + }; + + let h = Header { + parent_hash: [69u8; 32], + number: 1, + state_root: hex!("2481853da20b9f4322f34650fea5f240dcbfb266d02db94bfa0153c31f4a29db"), + transaction_root: hex!("91fab88ad8c30a6d05ad8e0cf9ab139bf1b8cdddc69abd51cdfa6d2699038af1"), + digest: Digest { logs: vec![], }, + }; + + let b = Block { + header: h, + transactions: vec![tx], + }; + + with_externalities(&mut t, || { + execute_block(b); + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 69); + }); + } + + #[test] + #[should_panic] + fn block_import_of_bad_state_root_fails() { + let one = one(); + let two = two(); + + let mut t = new_test_ext(); + + let tx = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 0, + function: Function::StakingTransfer, + input_data: vec![].join(&two).join(&69u64), + }, + signature: "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a".convert(), + }; + // tx: 2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000 + // sig: 679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a + + let h = Header { + parent_hash: [69u8; 32], + number: 1, + state_root: [0u8; 32], + transaction_root: [0u8; 32], // Unchecked currently. + digest: Digest { logs: vec![], }, + }; + + let b = Block { + header: h, + transactions: vec![tx], + }; + + with_externalities(&mut t, || { + execute_block(b); + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 69); + }); + } } diff --git a/wasm-runtime/polkadot/src/runtime/timestamp.rs b/wasm-runtime/polkadot/src/runtime/timestamp.rs index f0a798d112419..abc3fab5b9ef7 100644 --- a/wasm-runtime/polkadot/src/runtime/timestamp.rs +++ b/wasm-runtime/polkadot/src/runtime/timestamp.rs @@ -42,10 +42,9 @@ mod tests { use super::*; use super::public::*; - use runtime_std::{with_externalities, twox_128}; + use runtime_std::{with_externalities, twox_128, TestExternalities}; use runtime::timestamp; use codec::{Joiner, KeyedVec}; - use support::TestExternalities; #[test] fn timestamp_works() { diff --git a/wasm-runtime/polkadot/src/support/mod.rs b/wasm-runtime/polkadot/src/support/mod.rs index 6bc60880a07b6..ce9961272f485 100644 --- a/wasm-runtime/polkadot/src/support/mod.rs +++ b/wasm-runtime/polkadot/src/support/mod.rs @@ -31,4 +31,4 @@ pub use self::hashable::Hashable; #[cfg(feature = "with-std")] pub use self::statichex::{StaticHexConversion, StaticHexInto}; #[cfg(feature = "with-std")] -pub use self::testing::{AsBytesRef, HexDisplay, TestExternalities, one, two}; +pub use self::testing::{AsBytesRef, HexDisplay, one, two}; diff --git a/wasm-runtime/polkadot/src/support/storage.rs b/wasm-runtime/polkadot/src/support/storage.rs index 3ff8b1c75d4c9..a0f2a93b495b6 100644 --- a/wasm-runtime/polkadot/src/support/storage.rs +++ b/wasm-runtime/polkadot/src/support/storage.rs @@ -146,9 +146,8 @@ pub trait StorageVec { mod tests { use super::*; use std::collections::HashMap; - use runtime_std::with_externalities; - use support::{TestExternalities, HexDisplay}; - use runtime_std::{storage, twox_128}; + use support::HexDisplay; + use runtime_std::{storage, twox_128, TestExternalities, with_externalities}; #[test] fn integers_can_be_stored() { diff --git a/wasm-runtime/polkadot/src/support/testing.rs b/wasm-runtime/polkadot/src/support/testing.rs index e0e1a336c8042..a2c8579c96282 100644 --- a/wasm-runtime/polkadot/src/support/testing.rs +++ b/wasm-runtime/polkadot/src/support/testing.rs @@ -16,30 +16,9 @@ //! Testing helpers. -use std::collections::HashMap; -use runtime_std::{Externalities, ExternalitiesError}; use primitives::AccountID; use super::statichex::StaticHexInto; -#[derive(Debug, Default)] -/// Simple externaties implementation. -pub struct TestExternalities { - /// The storage map. - pub storage: HashMap, Vec>, -} - -impl Externalities for TestExternalities { - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { - Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) - } - - fn set_storage(&mut self, key: Vec, value: Vec) { - self.storage.insert(key, value); - } - - fn chain_id(&self) -> u64 { 42 } -} - #[macro_export] macro_rules! map { ($( $name:expr => $value:expr ),*) => ( diff --git a/wasm-runtime/std/src/lib.rs b/wasm-runtime/std/src/lib.rs index 9b7d385e694e6..3feb8c5544644 100644 --- a/wasm-runtime/std/src/lib.rs +++ b/wasm-runtime/std/src/lib.rs @@ -42,6 +42,8 @@ extern "C" { fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32; + fn ext_storage_root(result: *mut u8); + fn ext_enumerated_trie_root(values_data: *const u8, values_len: u32, lens_data: *const u32, lens_len: u32, result: *mut u8); fn ext_chain_id() -> u64; fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); fn ext_twox_128(data: *const u8, len: u32, out: *mut u8); @@ -72,6 +74,30 @@ pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> us } } +/// The current storage's root. +pub fn storage_root() -> [u8; 32] { + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_storage_root(result.as_mut_ptr()); + } + result +} + +/// A trie root calculated from enumerated values. +pub fn enumerated_trie_root(values: &[&[u8]]) -> [u8; 32] { + let lens = values.iter().map(|v| (v.len() as u32).to_le()).collect::>(); + let values = values.iter().fold(Vec::new(), |mut acc, sl| { acc.extend_from_slice(sl); acc }); + let mut result: [u8; 32] = Default::default(); + unsafe { + ext_enumerated_trie_root( + values.as_ptr(), values.len() as u32, + lens.as_ptr(), lens.len() as u32, + result.as_mut_ptr() + ); + } + result +} + /// The current relay chain identifier. pub fn chain_id() -> u64 { unsafe { @@ -82,7 +108,6 @@ pub fn chain_id() -> u64 { /// Conduct a 256-bit Blake2 hash. pub fn blake2_256(data: &[u8]) -> [u8; 32] { let mut result: [u8; 32] = Default::default(); - // guaranteed to write into result. unsafe { ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); } @@ -92,7 +117,6 @@ pub fn blake2_256(data: &[u8]) -> [u8; 32] { /// Conduct four XX hashes to give a 256-bit result. pub fn twox_256(data: &[u8]) -> [u8; 32] { let mut result: [u8; 32] = Default::default(); - // guaranteed to write into result. unsafe { ext_twox_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); } @@ -102,7 +126,6 @@ pub fn twox_256(data: &[u8]) -> [u8; 32] { /// Conduct two XX hashes to give a 128-bit result. pub fn twox_128(data: &[u8]) -> [u8; 16] { let mut result: [u8; 16] = Default::default(); - // guaranteed to write into result. unsafe { ext_twox_128(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); } diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm index db7dab9917d32..36fe893442dab 100644 Binary files a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm differ diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm index a0dc5a2754ab3..bd48f63571704 100644 Binary files a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm differ diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index 63e7666aadfbf..3905e9d911395 100644 Binary files a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm index c1b257f3a3135..3a909f06b3487 100644 Binary files a/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/wasm-runtime/test/src/lib.rs b/wasm-runtime/test/src/lib.rs index c63ca73fca595..ae5a6c179cd40 100644 --- a/wasm-runtime/test/src/lib.rs +++ b/wasm-runtime/test/src/lib.rs @@ -8,7 +8,8 @@ use alloc::vec::Vec; #[macro_use] extern crate runtime_std; -use runtime_std::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify}; +use runtime_std::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify, + enumerated_trie_root}; fn test_blake2_256(input: &[u8]) -> Vec { blake2_256(&input).to_vec() @@ -29,6 +30,10 @@ fn test_ed25519_verify(input: &[u8]) -> Vec { [ed25519_verify(sig, &msg[..], pubkey) as u8].to_vec() } +fn test_enumerated_trie_root(_input: &[u8]) -> Vec { + enumerated_trie_root(&[&b"zero"[..], &b"one"[..], &b"two"[..]]).to_vec() +} + fn test_data_in(input: &[u8]) -> Vec { print("set_storage"); set_storage(b"input", &input); @@ -59,4 +64,4 @@ fn test_conditional_panic(input: &[u8]) -> Vec { } impl_stubs!(test_data_in, test_empty_return, test_panic, test_conditional_panic, - test_blake2_256, test_twox_256, test_twox_128, test_ed25519_verify); + test_blake2_256, test_twox_256, test_twox_128, test_ed25519_verify, test_enumerated_trie_root);