diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index a05893e615..c741c845a5 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -553,7 +553,7 @@ dependencies = [ name = "native-runtime" version = "0.1.0" dependencies = [ - "runtime-support 0.1.0", + "runtime-std 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -726,10 +726,13 @@ 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", "polkadot-serializer 0.1.0", "polkadot-state-machine 0.1.0", + "runtime-std 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -913,7 +916,7 @@ dependencies = [ ] [[package]] -name = "runtime-support" +name = "runtime-std" version = "0.1.0" dependencies = [ "environmental 0.1.0", diff --git a/substrate/build.sh b/substrate/build.sh new file mode 100755 index 0000000000..40a1043226 --- /dev/null +++ b/substrate/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cd wasm-runtime +./build.sh +cd .. + diff --git a/substrate/executor/Cargo.toml b/substrate/executor/Cargo.toml index 0401bd4c77..5329eb7023 100644 --- a/substrate/executor/Cargo.toml +++ b/substrate/executor/Cargo.toml @@ -13,6 +13,9 @@ serde_derive = "1.0" parity-wasm = "0.15.0" 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" } [dev-dependencies] assert_matches = "1.1" diff --git a/substrate/executor/src/error.rs b/substrate/executor/src/error.rs index 4fd0ec0604..973fa58d38 100644 --- a/substrate/executor/src/error.rs +++ b/substrate/executor/src/error.rs @@ -17,7 +17,6 @@ //! Rust executor possible errors. use serializer; -use state_machine; error_chain! { foreign_links { @@ -38,9 +37,9 @@ error_chain! { } /// Externalities have failed. - Externalities(e: Box) { + Externalities { description("externalities failure"), - display("Externalities error: {}", e), + display("Externalities error"), } /// Invalid index. @@ -60,5 +59,11 @@ error_chain! { description("runtime failure"), display("Runtime error"), } + + /// Runtime failed. + InvalidMemoryReference { + description("invalid memory reference"), + display("Invalid memory reference"), + } } } diff --git a/substrate/executor/src/lib.rs b/substrate/executor/src/lib.rs index f1da582538..9935f8aec8 100644 --- a/substrate/executor/src/lib.rs +++ b/substrate/executor/src/lib.rs @@ -34,6 +34,9 @@ extern crate serde; extern crate parity_wasm; extern crate byteorder; extern crate rustc_hex; +extern crate native_runtime; +extern crate runtime_std; +extern crate libc; #[macro_use] extern crate error_chain; @@ -44,10 +47,11 @@ extern crate assert_matches; #[macro_use] mod wasm_utils; mod wasm_executor; +mod native_executor; pub mod error; /// Creates new RustExecutor for contracts. -pub fn executor() -> wasm_executor::WasmExecutor { - wasm_executor::WasmExecutor::default() +pub fn executor() -> native_executor::NativeExecutor { + native_executor::NativeExecutor } diff --git a/substrate/executor/src/native_executor.rs b/substrate/executor/src/native_executor.rs new file mode 100644 index 0000000000..b8c90f2d9a --- /dev/null +++ b/substrate/executor/src/native_executor.rs @@ -0,0 +1,110 @@ +use std::panic::catch_unwind; +use primitives::contract::CallData; +use state_machine::{Externalities, CodeExecutor}; +use error::{Error, ErrorKind, Result}; +use wasm_executor::WasmExecutor; +use native_runtime as runtime; +use runtime_std; + +pub struct NativeExecutor; + +fn safe_call Vec>(f: F) -> Result> { + catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()) +} + +impl CodeExecutor for NativeExecutor { + type Error = Error; + + fn call( + &self, + ext: &mut E, + code: &[u8], + method: &str, + data: &CallData, + ) -> Result> { + // WARNING!!! This assumes that the runtime was built *before* the main project. Until we + // get a proper build script, this must be strictly adhered to or things will go wrong. + let native_equivalent = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm"); + if code == &native_equivalent[..] { + runtime_std::with_externalities(ext, || match method { + "execute_block" => safe_call(|| runtime::execute_block(&data.0)), + "execute_transaction" => safe_call(|| runtime::execute_transaction(&data.0)), + _ => Err(ErrorKind::MethodNotFound(method.to_owned()).into()), + }) + } else { + // call into wasm. + WasmExecutor.call(ext, code, method, data) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use native_runtime::codec::KeyedVec; + use native_runtime::support::{TestExternalities, one, two, StaticHexInto}; + use native_runtime::runtime::staking::balance; + use primitives::twox_128; + + const BLOATY_CODE: &[u8] = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm"); + const COMPACT_CODE: &[u8] = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm"); + fn tx() -> Vec { "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000".convert() } + + #[test] + fn panic_execution_with_foreign_code_gives_error() { + let one = one(); + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let r = NativeExecutor.call(&mut t, BLOATY_CODE, "execute_transaction", &CallData(tx())); + assert!(r.is_err()); + } + + #[test] + fn panic_execution_with_native_equivalent_code_gives_error() { + let one = one(); + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let r = NativeExecutor.call(&mut t, COMPACT_CODE, "execute_transaction", &CallData(tx())); + assert!(r.is_err()); + } + + #[test] + fn successful_execution_with_native_equivalent_code_gives_ok() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let r = NativeExecutor.call(&mut t, COMPACT_CODE, "execute_transaction", &CallData(tx())); + assert!(r.is_ok()); + + runtime_std::with_externalities(&mut t, || { + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); + }); + } + + #[test] + fn successful_execution_with_foreign_code_gives_ok() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let r = NativeExecutor.call(&mut t, BLOATY_CODE, "execute_transaction", &CallData(tx())); + assert!(r.is_ok()); + + runtime_std::with_externalities(&mut t, || { + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); + }); + } +} diff --git a/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs index 7fc9a741f8..439defd449 100644 --- a/substrate/executor/src/wasm_executor.rs +++ b/substrate/executor/src/wasm_executor.rs @@ -17,9 +17,10 @@ //! Rust implementation of Polkadot contracts. use std::sync::Arc; +use std::cmp::Ordering; use std::collections::HashMap; use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; -use parity_wasm::interpreter::{ItemIndex}; +use parity_wasm::interpreter::{ItemIndex, DummyUserError}; use parity_wasm::RuntimeValue::{I32, I64}; use primitives::contract::CallData; use state_machine::{Externalities, CodeExecutor}; @@ -27,6 +28,7 @@ use error::{Error, ErrorKind, Result}; use wasm_utils::{MemoryInstance, UserDefinedElements, AddModuleWithoutFullDependentInstance}; use primitives::{ed25519, blake2_256, twox_128, twox_256}; +use primitives::hexdisplay::HexDisplay; struct Heap { end: u32, @@ -77,16 +79,33 @@ impl WritePrimitive for MemoryInstance { } impl_function_executor!(this: FunctionExecutor<'e, E>, - ext_print(utf8_data: *const u8, utf8_len: u32) => { + ext_print_utf8(utf8_data: *const u8, utf8_len: u32) => { if let Ok(utf8) = this.memory.get(utf8_data, utf8_len as usize) { if let Ok(message) = String::from_utf8(utf8) { println!("Runtime: {}", message); } } }, + ext_print_hex(data: *const u8, len: u32) => { + if let Ok(hex) = this.memory.get(data, len as usize) { + println!("Runtime: {}", HexDisplay::from(&hex)); + } + }, ext_print_num(number: u64) => { 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()); + } + }, 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); println!("memcpy {} from {}, {} bytes", dest, src, count); @@ -128,11 +147,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.write_primitive(written_out, written); offset as u32 }, - ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32 => { + 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[0..written]); + let _ = this.memory.set(value_data, &value[..written]); written as u32 } else { 0 } } else { 0 } @@ -141,47 +161,63 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.ext.chain_id() }, ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { - let result = - if let Ok(value) = this.memory.get(data, len as usize) { - twox_128(&value) - } else { - [0; 16] - }; + 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) + } else { + [0; 16] + }; let _ = this.memory.set(out, &result); }, ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { - let result = - if let Ok(value) = this.memory.get(data, len as usize) { - twox_256(&value) - } else { - [0; 32] - }; + 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_256(&value) + } else { + [0; 32] + }; let _ = this.memory.set(out, &result); }, ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { - let result = - if let Ok(value) = this.memory.get(data, len as usize) { - blake2_256(&value) - } else { - [0; 32] - }; + 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) + } else { + [0; 32] + }; let _ = this.memory.set(out, &result); }, 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 0; + return 2; }; let mut pubkey = [0u8; 32]; if let Err(_) = this.memory.get_into(pubkey_data, &mut pubkey[..]) { - return 0; + 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)) { 1 } else { 0 } + if ed25519::Signature::from(sig).verify(&msg, &ed25519::Public::from(pubkey)) { + 0 + } else { + 5 + } } else { - 0 + 4 } })() } @@ -224,7 +260,9 @@ impl CodeExecutor for WasmExecutor { .add_argument(I32(offset as i32)) .add_argument(I32(size as i32))) .and_then(|p| module.execute_export(method, p)) - .map_err(|_| -> Error { ErrorKind::Runtime.into() })?; + .map_err(|_| -> Error { + ErrorKind::Runtime.into() + })?; if let Some(I64(r)) = returned { memory.get(r as u32, (r >> 32) as u32 as usize) @@ -240,23 +278,31 @@ 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 native_runtime::codec::KeyedVec; + use native_runtime::runtime::staking::balance; - #[derive(Debug, Default)] - struct TestExternalities { - storage: HashMap, Vec>, + #[test] + fn returning_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); + + let output = WasmExecutor.call(&mut ext, &test_code[..], "test_empty_return", &CallData(vec![])).unwrap(); + assert_eq!(output, vec![0u8; 0]); } - impl Externalities for TestExternalities { - type Error = Error; - fn storage(&self, key: &[u8]) -> Result<&[u8]> { - Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) - } + #[test] + fn panicking_should_work() { + let mut ext = TestExternalities::default(); + let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - fn set_storage(&mut self, key: Vec, value: Vec) { - self.storage.insert(key, value); - } + let output = WasmExecutor.call(&mut ext, &test_code[..], "test_panic", &CallData(vec![])); + assert!(output.is_err()); - fn chain_id(&self) -> u64 { 42 } + let output = WasmExecutor.call(&mut ext, &test_code[..], "test_conditional_panic", &CallData(vec![2])); + assert!(output.is_err()); } #[test] @@ -283,11 +329,11 @@ mod tests { 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_blake2_256", &CallData(b"".to_vec())).unwrap(), - FromHex::from_hex("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8").unwrap() + blake2_256(&b""[..]).to_vec() ); assert_eq!( WasmExecutor.call(&mut ext, &test_code[..], "test_blake2_256", &CallData(b"Hello world!".to_vec())).unwrap(), - FromHex::from_hex("3fbc092db9350757e2ab4f7ee9792bfcd2f5220ada5a4bc684487f60c6034369").unwrap() + blake2_256(&b"Hello world!"[..]).to_vec() ); } @@ -330,7 +376,40 @@ mod tests { calldata.extend_from_slice(sig.as_ref()); assert_eq!( WasmExecutor.call(&mut ext, &test_code[..], "test_ed25519_verify", &CallData(calldata)).unwrap(), - vec![1] + vec![0] ); } + + fn tx() -> Vec { "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000".convert() } + + #[test] + fn panic_execution_gives_error() { + let one = one(); + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let foreign_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm"); + let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &CallData(tx())); + assert!(r.is_err()); + } + + #[test] + fn successful_execution_gives_ok() { + let one = one(); + let two = two(); + + let mut t = TestExternalities { storage: map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + let foreign_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm"); + let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &CallData(tx())); + assert!(r.is_ok()); + + runtime_std::with_externalities(&mut t, || { + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); + }); + } } diff --git a/substrate/native-runtime/Cargo.toml b/substrate/native-runtime/Cargo.toml index eb5eb82408..25091c2cef 100644 --- a/substrate/native-runtime/Cargo.toml +++ b/substrate/native-runtime/Cargo.toml @@ -9,5 +9,5 @@ with-std = [] without-std = [] [dependencies] -runtime-support = { path = "./support", version = "0.1" } +runtime-std = { path = "./std", version = "0.1" } rustc-hex = "1.0" diff --git a/substrate/native-runtime/support/Cargo.toml b/substrate/native-runtime/std/Cargo.toml similarity index 93% rename from substrate/native-runtime/support/Cargo.toml rename to substrate/native-runtime/std/Cargo.toml index 1c24c014e6..0976e4b378 100644 --- a/substrate/native-runtime/support/Cargo.toml +++ b/substrate/native-runtime/std/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "runtime-support" +name = "runtime-std" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/substrate/native-runtime/support/src/lib.rs b/substrate/native-runtime/std/src/lib.rs similarity index 70% rename from substrate/native-runtime/support/src/lib.rs rename to substrate/native-runtime/std/src/lib.rs index 91005a2f1d..fb74093d18 100644 --- a/substrate/native-runtime/support/src/lib.rs +++ b/substrate/native-runtime/std/src/lib.rs @@ -39,7 +39,8 @@ pub mod prelude { pub use std::boxed::Box; } -pub use polkadot_state_machine::Externalities; +pub use polkadot_state_machine::{Externalities, ExternalitiesError}; +use primitives::hexdisplay::HexDisplay; // TODO: use the real error, not NoError. @@ -50,29 +51,34 @@ impl fmt::Display for NoError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } -environmental!(ext : Externalities + 'static); +environmental!(ext : trait Externalities); /// Get `key` from storage and return a `Vec`, empty if there's a problem. pub fn storage(key: &[u8]) -> Vec { ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec())) - .unwrap_or(None) - .unwrap_or_else(|| vec![]) + .expect("read_storage cannot be called outside of an Externalities-provided environment.") + .unwrap_or_else(Vec::new) } /// Get `key` from storage, placing the value into `value_out` (as much as possible) and return /// the number of bytes that the key in storage was. -pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize { ext::with(|ext| { if let Ok(value) = ext.storage(key) { + let value = &value[value_offset..]; let written = ::std::cmp::min(value.len(), value_out.len()); value_out[0..written].copy_from_slice(&value[0..written]); value.len() } else { + // no-entry is treated as an empty vector of bytes. + // TODO: consider allowing empty-vector to exist separately to no-entry (i.e. return + // Option) 0 } - }).unwrap_or(0) + }).expect("read_storage cannot be called outside of an Externalities-provided environment.") } +/// Set the storage to some particular key. pub fn set_storage(key: &[u8], value: &[u8]) { ext::with(|ext| ext.set_storage(key.to_vec(), value.to_vec()) @@ -96,10 +102,36 @@ pub fn ed25519_verify(sig: &[u8; 64], msg: &[u8], pubkey: &[u8; 32]) -> bool { /// Execute the given closure with global function available whose functionality routes into the /// externalities `ext`. Forwards the value that the closure returns. -pub fn with_externalities R>(ext: &mut (Externalities + 'static), f: F) -> R { +pub fn with_externalities R>(ext: &mut Externalities, f: F) -> R { ext::using(ext, f) } +pub trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + println!("Runtime: {}", HexDisplay::from(&self)); + } +} + +impl<'a> Printable for &'a str { + fn print(self) { + println!("Runtime: {}", self); + } +} + +impl Printable for u64 { + fn print(self) { + println!("Runtime: {}", self); + } +} + +pub fn print(value: T) { + value.print(); +} + #[macro_export] macro_rules! impl_stubs { ($( $name:ident ),*) => {} @@ -115,9 +147,7 @@ mod tests { storage: HashMap, Vec>, } impl Externalities for TestExternalities { - type Error = NoError; - - fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> { + fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } @@ -154,4 +184,20 @@ mod tests { false })); } + + #[test] + fn read_storage_works() { + let mut t = TestExternalities { storage: map![ + b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec() + ], }; + + with_externalities(&mut t, || { + let mut v = [0u8; 4]; + assert!(read_storage(b":test", &mut v[..], 0) >= 4); + assert_eq!(v, [11u8, 0, 0, 0]); + let mut w = [0u8; 11]; + assert!(read_storage(b":test", &mut w[..], 4) >= 11); + assert_eq!(&w, b"Hello world"); + }); + } } diff --git a/substrate/primitives/src/hexdisplay.rs b/substrate/primitives/src/hexdisplay.rs index d6299a2e41..fa62d69869 100644 --- a/substrate/primitives/src/hexdisplay.rs +++ b/substrate/primitives/src/hexdisplay.rs @@ -39,6 +39,10 @@ pub trait AsBytesRef { fn as_bytes_ref(&self) -> &[u8]; } +impl<'a> AsBytesRef for &'a [u8] { + fn as_bytes_ref(&self) -> &[u8] { self } +} + impl AsBytesRef for [u8] { fn as_bytes_ref(&self) -> &[u8] { &self } } diff --git a/substrate/state_machine/src/ext.rs b/substrate/state_machine/src/ext.rs index 1452558ccd..764c51de10 100644 --- a/substrate/state_machine/src/ext.rs +++ b/substrate/state_machine/src/ext.rs @@ -19,7 +19,7 @@ use std::{error, fmt}; use backend::Backend; -use {Externalities, OverlayedChanges}; +use {Externalities, ExternalitiesError, OverlayedChanges}; /// Errors that can occur when interacting with the externalities. #[derive(Debug, Copy, Clone)] @@ -61,12 +61,10 @@ pub struct Ext<'a, B: 'a> { impl<'a, B: 'a> Externalities for Ext<'a, B> where B: Backend { - type Error = B::Error; - - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { + fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { match self.overlay.storage(key) { Some(x) => Ok(x), - None => self.backend.storage(key) + None => self.backend.storage(key).map_err(|_| ExternalitiesError), } } diff --git a/substrate/state_machine/src/lib.rs b/substrate/state_machine/src/lib.rs index cd25cdcb90..cd7f9291bb 100644 --- a/substrate/state_machine/src/lib.rs +++ b/substrate/state_machine/src/lib.rs @@ -113,6 +113,18 @@ impl OverlayedChanges { pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} +/// Externalities Error. +/// +/// Externalities are not really allowed to have errors, since it's assumed that dependent code +/// would not be executed unless externalities were available. This is included for completeness, +/// and as a transition away from the pre-existing framework. +#[derive(Debug, Eq, PartialEq)] +pub struct ExternalitiesError; + +impl fmt::Display for ExternalitiesError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Externalities Error") } +} + fn to_keyed_vec(value: u32, mut prepend: Vec) -> Vec { prepend.extend((0..::std::mem::size_of::()).into_iter().map(|i| (value >> (i * 8)) as u8)); prepend @@ -120,11 +132,8 @@ fn to_keyed_vec(value: u32, mut prepend: Vec) -> Vec { /// Externalities: pinned to specific active address. pub trait Externalities { - /// Externalities error type. - type Error: Error; - /// Read storage of current contract being called. - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; + fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError>; /// Set storage of current contract being called (effective immediately). fn set_storage(&mut self, key: Vec, value: Vec); @@ -133,7 +142,7 @@ pub trait Externalities { fn chain_id(&self) -> u64; /// Get the current set of authorities from storage. - fn authorities(&self) -> Result, Self::Error> { + fn authorities(&self) -> Result, ExternalitiesError> { (0..self.storage(b"con:aut:len")?.into_iter() .rev() .fold(0, |acc, &i| (acc << 8) + (i as u32))) @@ -202,7 +211,7 @@ pub fn execute( #[cfg(test)] mod tests { use std::collections::HashMap; - use super::{OverlayedChanges, Externalities}; + use super::{OverlayedChanges, Externalities, ExternalitiesError}; #[test] fn overlayed_storage_works() { @@ -234,9 +243,7 @@ mod tests { storage: HashMap, Vec>, } impl Externalities for TestExternalities { - type Error = u8; - - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { + fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } diff --git a/substrate/wasm-runtime/Cargo.lock b/substrate/wasm-runtime/Cargo.lock index d134139a3c..15ccc67c64 100644 --- a/substrate/wasm-runtime/Cargo.lock +++ b/substrate/wasm-runtime/Cargo.lock @@ -13,11 +13,11 @@ version = "0.1.0" name = "runtime-polkadot" version = "0.1.0" dependencies = [ - "runtime-support 0.1.0", + "runtime-std 0.1.0", ] [[package]] -name = "runtime-support" +name = "runtime-std" version = "0.1.0" dependencies = [ "pwasm-alloc 0.1.0", @@ -28,6 +28,6 @@ dependencies = [ name = "runtime-test" version = "0.1.0" dependencies = [ - "runtime-support 0.1.0", + "runtime-std 0.1.0", ] diff --git a/substrate/wasm-runtime/build.sh b/substrate/wasm-runtime/build.sh index c3030f99d3..83f46d5a04 100755 --- a/substrate/wasm-runtime/build.sh +++ b/substrate/wasm-runtime/build.sh @@ -1,7 +1,8 @@ #!/bin/sh +set -e cargo +nightly build --target=wasm32-unknown-unknown --release -dirs=`find * -maxdepth 0 -type d | grep -v pwasm- | grep -v support` +dirs=`find * -maxdepth 0 -type d | grep -v pwasm- | grep -v std` for i in $dirs do if [[ -e $i/Cargo.toml ]] diff --git a/substrate/wasm-runtime/polkadot/Cargo.toml b/substrate/wasm-runtime/polkadot/Cargo.toml index ce2d9909a7..669ccac6fa 100644 --- a/substrate/wasm-runtime/polkadot/Cargo.toml +++ b/substrate/wasm-runtime/polkadot/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Parity Technologies "] crate-type = ["cdylib"] [dependencies] -runtime-support = { path = "../support", version = "0.1" } +runtime-std = { path = "../std", version = "0.1" } [features] default = ["without-std"] diff --git a/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs b/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs index c4978d5ce4..1b550edffa 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs @@ -17,7 +17,9 @@ //! Endian manager. /// Trait to allow conversion to a know endian representation when sensitive. -// note: the copy bound and static lifetimes are necessary for safety of `Slicable` blanket implementation. +/// Types implementing this trait must have a size > 0. +// note: the copy bound and static lifetimes are necessary for safety of `Slicable` blanket +// implementation. pub trait EndianSensitive: Copy + 'static { fn to_le(self) -> Self { self } fn to_be(self) -> Self { self } @@ -45,6 +47,9 @@ macro_rules! impl_non_endians { )* } } +// NOTE: See test to ensure correctness. +impl EndianSensitive for bool {} + impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], @@ -65,4 +70,12 @@ mod tests { fn _takes_static() { } fn _takes_endian_sensitive() { _takes_static::() } } + + #[test] + fn bool_is_not_endian_sensitive() { + let b = true; + assert_eq!(b.to_be(), b.to_le()); + let b = false; + assert_eq!(b.to_be(), b.to_le()); + } } diff --git a/substrate/wasm-runtime/polkadot/src/codec/joiner.rs b/substrate/wasm-runtime/polkadot/src/codec/joiner.rs index 7fdb1e1645..a7a9f618eb 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/joiner.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/joiner.rs @@ -16,8 +16,8 @@ //! Vec serialiser. -use runtime_support::prelude::*; -use slicable::Slicable; +use runtime_std::prelude::*; +use super::slicable::Slicable; /// Trait to allow itself to be serialised into a `Vec` pub trait Joiner { diff --git a/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs b/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs index 210606ab7c..2c55cb5648 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs @@ -16,8 +16,8 @@ //! Serialiser and prepender. -use runtime_support::prelude::*; -use slicable::Slicable; +use runtime_std::prelude::*; +use super::slicable::Slicable; /// Trait to allow itselg to be serialised and prepended by a given slice. pub trait KeyedVec { diff --git a/substrate/wasm-runtime/polkadot/src/codec/mod.rs b/substrate/wasm-runtime/polkadot/src/codec/mod.rs index 7d9bc90c4f..94afc574c1 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/mod.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/mod.rs @@ -16,8 +16,14 @@ //! Codec utils. -pub mod endiansensitive; -pub mod streamreader; -pub mod joiner; -pub mod slicable; -pub mod keyedvec; +mod endiansensitive; +mod slicable; +mod streamreader; +mod joiner; +mod keyedvec; + +pub use self::endiansensitive::EndianSensitive; +pub use self::slicable::{Slicable, NonTrivialSlicable}; +pub use self::streamreader::StreamReader; +pub use self::joiner::Joiner; +pub use self::keyedvec::KeyedVec; diff --git a/substrate/wasm-runtime/polkadot/src/codec/slicable.rs b/substrate/wasm-runtime/polkadot/src/codec/slicable.rs index 40f8a532b7..0f1ef812a6 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/slicable.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/slicable.rs @@ -16,16 +16,18 @@ //! Serialisation. -use runtime_support::prelude::*; -use runtime_support::{mem, slice}; -use joiner::Joiner; -use endiansensitive::EndianSensitive; +use runtime_std::prelude::*; +use runtime_std::{mem, slice}; +use super::joiner::Joiner; +use super::endiansensitive::EndianSensitive; /// Trait that allows zero-copy read/write of value-references to/from slices in LE format. pub trait Slicable: Sized { fn from_slice(value: &[u8]) -> Option { - Self::set_as_slice(|out| if value.len() == out.len() { - out.copy_from_slice(&value); + Self::set_as_slice(&|out, offset| if value.len() >= out.len() + offset { + let value = &value[offset..]; + let len = out.len(); + out.copy_from_slice(&value[0..len]); true } else { false @@ -34,7 +36,7 @@ pub trait Slicable: Sized { fn to_vec(&self) -> Vec { self.as_slice_then(|s| s.to_vec()) } - fn set_as_slice bool>(set_slice: F) -> Option; + fn set_as_slice bool>(set_slice: &F) -> Option; fn as_slice_then R>(&self, f: F) -> R { f(&self.to_vec()) } @@ -45,14 +47,15 @@ pub trait Slicable: Sized { pub trait NonTrivialSlicable: Slicable {} impl Slicable for T { - fn set_as_slice bool>(fill_slice: F) -> Option { + fn set_as_slice bool>(fill_slice: &F) -> Option { let size = mem::size_of::(); + assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); let mut result: T = unsafe { mem::zeroed() }; let result_slice = unsafe { let ptr = &mut result as *mut _ as *mut u8; slice::from_raw_parts_mut(ptr, size) }; - if fill_slice(result_slice) { + if fill_slice(result_slice, 0) { Some(result.from_le()) } else { None @@ -60,6 +63,7 @@ impl Slicable for T { } fn as_slice_then R>(&self, f: F) -> R { let size = mem::size_of::(); + assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); self.as_le_then(|le| { let value_slice = unsafe { let ptr = le as *const _ as *const u8; @@ -77,8 +81,16 @@ impl Slicable for Vec { fn from_slice(value: &[u8]) -> Option { Some(value[4..].to_vec()) } - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); + fn set_as_slice bool>(fill_slice: &F) -> Option { + u32::set_as_slice(fill_slice).and_then(|len| { + let mut v = Vec::with_capacity(len as usize); + unsafe { v.set_len(len as usize); } + if fill_slice(&mut v, 4) { + Some(v) + } else { + None + } + }) } fn to_vec(&self) -> Vec { let mut r: Vec = Vec::new().join(&(self.len() as u32)); @@ -89,3 +101,48 @@ impl Slicable for Vec { u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) } } + +impl NonTrivialSlicable for Vec where Vec: Slicable {} + +impl Slicable for Vec { + fn from_slice(value: &[u8]) -> Option { + let len = Self::size_of(&value[0..4])?; + let mut off = 4; + let mut r = Vec::new(); + while off < len { + let element_len = T::size_of(&value[off..])?; + r.push(T::from_slice(&value[off..off + element_len])?); + off += element_len; + } + Some(r) + } + + fn set_as_slice bool>(_fill_slice: &F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + let vecs = self.iter().map(Slicable::to_vec).collect::>(); + let len = vecs.iter().fold(0, |mut a, v| {a += v.len(); a}); + let mut r = Vec::new().join(&(len as u32)); + vecs.iter().for_each(|v| r.extend_from_slice(v)); + r + } + + fn size_of(data: &[u8]) -> Option { + u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn vec_is_slicable() { + let v = b"Hello world".to_vec(); + v.as_slice_then(|ref slice| + assert_eq!(slice, &b"\x0b\0\0\0Hello world") + ); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/codec/streamreader.rs b/substrate/wasm-runtime/polkadot/src/codec/streamreader.rs index 33056a5ac3..b745f8afd9 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/streamreader.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/streamreader.rs @@ -16,7 +16,7 @@ //! Deserialiser. -use slicable::Slicable; +use super::slicable::Slicable; /// Simple deserialiser. pub struct StreamReader<'a> { diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index a08cc046d3..1eeff3c649 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -20,34 +20,32 @@ #![cfg_attr(feature = "strict", deny(warnings))] #[macro_use] -extern crate runtime_support; +extern crate runtime_std; -#[cfg(test)] +#[cfg(feature = "with-std")] extern crate rustc_hex; -mod codec; +pub mod codec; #[macro_use] -mod support; -mod runtime; -pub use codec::{endiansensitive, streamreader, joiner, slicable, keyedvec}; -pub use support::{primitives, function, environment, storable}; -#[cfg(test)] -pub use support::{testing, statichex}; +pub mod support; +pub mod primitives; +pub mod runtime; -use runtime_support::prelude::*; -use slicable::Slicable; +use runtime_std::prelude::*; +use codec::Slicable; use primitives::{Block, UncheckedTransaction}; /// Execute a block, with `input` being the canonical serialisation of the block. Returns the /// empty vector. -pub fn execute_block(input: Vec) -> Vec { - runtime::system::execute_block(Block::from_slice(&input).unwrap()); +pub fn execute_block(input: &[u8]) -> Vec { + runtime::system::internal::execute_block(Block::from_slice(input).unwrap()); Vec::new() } /// Execute a given, serialised, transaction. Returns the empty vector. -pub fn execute_transaction(input: Vec) -> Vec { - runtime::system::execute_transaction(&UncheckedTransaction::from_slice(&input).unwrap()); +pub fn execute_transaction(input: &[u8]) -> Vec { + let utx = UncheckedTransaction::from_slice(input).unwrap(); + runtime::system::internal::execute_transaction(&utx); Vec::new() } diff --git a/substrate/wasm-runtime/polkadot/src/primitives/block.rs b/substrate/wasm-runtime/polkadot/src/primitives/block.rs new file mode 100644 index 0000000000..d33b51609c --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/block.rs @@ -0,0 +1,58 @@ +// 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 . + +//! Block type. + +use runtime_std::prelude::*; +use codec::{StreamReader, Joiner, Slicable, NonTrivialSlicable}; +use primitives::{Header, UncheckedTransaction}; + +/// A Polkadot relay chain block. +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +pub struct Block { + /// The header of the block. + pub header: Header, + /// All transactions. + pub transactions: Vec, +} + +impl Slicable for Block { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Block { + header: reader.read()?, + transactions: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: &F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.header) + .join(&self.transactions) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = Header::size_of(data)?; + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for Block {} diff --git a/substrate/wasm-runtime/polkadot/src/primitives/digest.rs b/substrate/wasm-runtime/polkadot/src/primitives/digest.rs new file mode 100644 index 0000000000..18107460b0 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/digest.rs @@ -0,0 +1,27 @@ +// 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 . + +//! Digest type. + +use runtime_std::prelude::*; + +#[derive(Clone, Default)] +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +/// The digest of a block, useful for light-clients. +pub struct Digest { + /// All logs that have happened in the block. + pub logs: Vec>, +} diff --git a/substrate/wasm-runtime/polkadot/src/support/function.rs b/substrate/wasm-runtime/polkadot/src/primitives/function.rs similarity index 56% rename from substrate/wasm-runtime/polkadot/src/support/function.rs rename to substrate/wasm-runtime/polkadot/src/primitives/function.rs index f4ee9c79e6..e906cdc718 100644 --- a/substrate/wasm-runtime/polkadot/src/support/function.rs +++ b/substrate/wasm-runtime/polkadot/src/primitives/function.rs @@ -17,30 +17,34 @@ //! Function data: This describes a function that can be called from an external transaction. use primitives::AccountID; -use streamreader::StreamReader; -use runtime::{staking, session, timestamp}; +use codec::StreamReader; +use runtime::{staking, session, timestamp, governance}; -/// The functions that a transaction can call (and be dispatched to). -#[cfg_attr(test, derive(PartialEq, Debug))] +/// Public functions that can be dispatched to. #[derive(Clone, Copy)] +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +#[repr(u8)] pub enum Function { - StakingStake, - StakingUnstake, - StakingTransfer, - SessionSetKey, - TimestampSet, + StakingStake = 0, + StakingUnstake = 1, + StakingTransfer = 2, + SessionSetKey = 3, + TimestampSet = 4, + GovernancePropose = 5, + GovernanceApprove = 6, } impl Function { /// Derive `Some` value from a `u8`, or `None` if it's invalid. pub fn from_u8(value: u8) -> Option { - match value { - x if x == Function::StakingStake as u8 => Some(Function::StakingStake), - x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake), - x if x == Function::StakingTransfer as u8 => Some(Function::StakingTransfer), - x if x == Function::SessionSetKey as u8 => Some(Function::SessionSetKey), - x if x == Function::TimestampSet as u8 => Some(Function::TimestampSet), - _ => None, + use self::*; + let functions = [Function::StakingStake, Function::StakingUnstake, + Function::StakingTransfer, Function::SessionSetKey, Function::TimestampSet, + Function::GovernancePropose, Function::GovernanceApprove]; + if (value as usize) < functions.len() { + Some(functions[value as usize]) + } else { + None } } } @@ -51,23 +55,31 @@ impl Function { let mut params = StreamReader::new(data); match *self { Function::StakingStake => { - staking::stake(transactor); + staking::public::stake(transactor); } Function::StakingUnstake => { - staking::unstake(transactor); + staking::public::unstake(transactor); } Function::StakingTransfer => { let dest = params.read().unwrap(); let value = params.read().unwrap(); - staking::transfer(transactor, &dest, value); + staking::public::transfer(transactor, &dest, value); } Function::SessionSetKey => { let session = params.read().unwrap(); - session::set_key(transactor, &session); + session::public::set_key(transactor, &session); } Function::TimestampSet => { let t = params.read().unwrap(); - timestamp::set(t); + timestamp::public::set(t); + } + Function::GovernancePropose => { + let proposal = params.read().unwrap(); + governance::public::propose(transactor, &proposal); + } + Function::GovernanceApprove => { + let era_index = params.read().unwrap(); + governance::public::approve(transactor, era_index); } } } diff --git a/substrate/wasm-runtime/polkadot/src/primitives/header.rs b/substrate/wasm-runtime/polkadot/src/primitives/header.rs new file mode 100644 index 0000000000..c97d5ebf29 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/header.rs @@ -0,0 +1,72 @@ +// 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 . + +//! Block header type. + +use runtime_std::prelude::*; +use codec::{StreamReader, Joiner, Slicable, NonTrivialSlicable}; +use runtime_std::mem; +use primitives::{BlockNumber, Hash, Digest}; + +#[derive(Clone)] +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +/// The header for a block. +pub struct Header { + /// The parent block's "hash" (actually the Blake2-256 hash of its serialised header). + pub parent_hash: Hash, + /// The block's number (how many ancestors does it have?). + pub number: BlockNumber, + /// The root of the trie that represents this block's final storage map. + pub state_root: Hash, + /// The root of the trie that represents this block's transactions, indexed by a 32-bit integer. + pub transaction_root: Hash, + /// The digest for this block. + pub digest: Digest, +} + +impl Slicable for Header { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Header { + parent_hash: reader.read()?, + number: reader.read()?, + state_root: reader.read()?, + transaction_root: reader.read()?, + digest: Digest { logs: reader.read()?, }, + }) + } + + fn set_as_slice bool>(_fill_slice: &F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.parent_hash) + .join(&self.number) + .join(&self.state_root) + .join(&self.transaction_root) + .join(&self.digest.logs) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = mem::size_of::() + mem::size_of::() + mem::size_of::() + mem::size_of::(); + let second_part = >>::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for Header {} diff --git a/substrate/wasm-runtime/polkadot/src/primitives/misc.rs b/substrate/wasm-runtime/polkadot/src/primitives/misc.rs new file mode 100644 index 0000000000..2d6954f9e7 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/misc.rs @@ -0,0 +1,41 @@ +// 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 . + +//! Miscellaneous small types. + +/// The Ed25519 pubkey that identifies an account. +pub type AccountID = [u8; 32]; + +/// Virtual account ID that represents the idea of a dispatch/statement being signed by everybody +/// (who matters). Essentially this means that a majority of validators have decided it is +/// "correct". +pub const EVERYBODY: AccountID = [255u8; 32]; + +/// The Ed25519 pub key of an session that belongs to an authority. This is used as what the +/// external environment/consensus algorithm calls an "authority". +pub type SessionKey = AccountID; + +/// Indentifier for a chain. +pub type ChainID = u64; + +/// Index of a block in the chain. +pub type BlockNumber = u64; + +/// Index of a transaction. +pub type TxOrder = u64; + +/// A hash of some data. +pub type Hash = [u8; 32]; diff --git a/substrate/wasm-runtime/polkadot/src/primitives/mod.rs b/substrate/wasm-runtime/polkadot/src/primitives/mod.rs new file mode 100644 index 0000000000..51ce3d121a --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/mod.rs @@ -0,0 +1,38 @@ +// 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 . + +//! Primitive types for the runtime. + +mod misc; +mod proposal; +mod function; +mod digest; +mod header; +mod transaction; +mod uncheckedtransaction; +mod block; + +#[cfg(test)] +mod tests; + +pub use self::misc::{AccountID, EVERYBODY, SessionKey, ChainID, BlockNumber, TxOrder, Hash}; +pub use self::proposal::{Proposal, InternalFunction}; +pub use self::function::Function; +pub use self::digest::Digest; +pub use self::header::Header; +pub use self::transaction::Transaction; +pub use self::uncheckedtransaction::UncheckedTransaction; +pub use self::block::Block; diff --git a/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs b/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs new file mode 100644 index 0000000000..1b8aa6b13e --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/proposal.rs @@ -0,0 +1,137 @@ +// 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 . + +//! Proposal: This describes a combination of a function ID and data that can be used to call into +//! an internal function. + +use runtime_std::prelude::*; +use runtime_std::mem; +use codec::{Slicable, Joiner, StreamReader}; +use runtime::{system, governance, staking, session}; + +/// Internal functions that can be dispatched to. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +#[repr(u8)] +pub enum InternalFunction { + SystemSetCode = 0, + StakingSetSessionsPerEra = 1, + StakingSetBondingDuration = 2, + StakingSetValidatorCount = 3, + GovernanceSetApprovalPpmRequired = 4, + SessionSetLength = 5, +} + +impl InternalFunction { + /// Derive `Some` value from a `u8`, or `None` if it's invalid. + pub fn from_u8(value: u8) -> Option { + use self::*; + let functions = [ + InternalFunction::SystemSetCode, + InternalFunction::StakingSetSessionsPerEra, + InternalFunction::StakingSetBondingDuration, + InternalFunction::StakingSetValidatorCount, + InternalFunction::GovernanceSetApprovalPpmRequired, + InternalFunction::SessionSetLength + ]; + if (value as usize) < functions.len() { + Some(functions[value as usize]) + } else { + None + } + } +} + +/// An internal function. +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +pub struct Proposal { + /// The priviledged function to call. + pub function: InternalFunction, + /// The serialised data to call it with. + pub input_data: Vec, +} + +impl Slicable for Proposal { + fn set_as_slice bool>(fill_slice: &F) -> Option { + Some(Proposal { + function: InternalFunction::from_u8(Slicable::set_as_slice(fill_slice)?)?, + input_data: Slicable::set_as_slice(&|s, o| fill_slice(s, o + 1))?, + }) + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&(self.function as u8)) + .join(&self.input_data) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = mem::size_of::(); + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl Proposal { + pub fn enact(&self) { + let mut params = StreamReader::new(&self.input_data); + match self.function { + InternalFunction::SystemSetCode => { + let code: Vec = params.read().unwrap(); + system::privileged::set_code(&code); + } + InternalFunction::StakingSetSessionsPerEra => { + let value = params.read().unwrap(); + staking::privileged::set_sessions_per_era(value); + } + InternalFunction::StakingSetBondingDuration => { + let value = params.read().unwrap(); + staking::privileged::set_bonding_duration(value); + } + InternalFunction::StakingSetValidatorCount => { + let value = params.read().unwrap(); + staking::privileged::set_validator_count(value); + } + InternalFunction::GovernanceSetApprovalPpmRequired => { + let value = params.read().unwrap(); + governance::privileged::set_approval_ppm_required(value); + } + InternalFunction::SessionSetLength => { + let value = params.read().unwrap(); + session::privileged::set_length(value); + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use support::StaticHexInto; + + #[test] + fn slicing_should_work() { + let p = Proposal { + function: InternalFunction::SystemSetCode, + input_data: b"Hello world".to_vec(), + }; + let v = p.to_vec(); + assert_eq!(v, "000b00000048656c6c6f20776f726c64".convert::>()); + + let o = Proposal::from_slice(&v).unwrap(); + assert_eq!(p, o); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/primitives/tests.rs b/substrate/wasm-runtime/polkadot/src/primitives/tests.rs new file mode 100644 index 0000000000..fdb600a5bb --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/tests.rs @@ -0,0 +1,241 @@ +// 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 . + +//! Tests. + +use super::*; +use runtime_std::prelude::*; +use codec::{Joiner, Slicable}; +use primitives::Function; + +#[test] +fn serialise_transaction_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx = Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }; + let serialised = tx.to_vec(); + assert_eq!(serialised, vec![ + 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0 + ]); +} + +#[test] +fn deserialise_transaction_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx = Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }; + let data = [ + 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0 + ]; + let deserialised = Transaction::from_slice(&data).unwrap(); + assert_eq!(deserialised, tx); +} + +#[test] +fn serialise_header_works() { + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let serialised = h.to_vec(); + assert_eq!(serialised, vec![ + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 + ]); +} + +#[test] +fn deserialise_header_works() { + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let data = [ + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 + ]; + let deserialised = Header::from_slice(&data).unwrap(); + assert_eq!(deserialised, h); +} + +#[test] +fn serialise_block_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx1 = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }, + signature: [1u8; 64], + }; + let tx2 = UncheckedTransaction { + transaction: Transaction { + signed: two.clone(), + nonce: 42, + function: Function::StakingStake, + input_data: Vec::new(), + }, + signature: [2u8; 64], + }; + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let b = Block { + header: h, + transactions: vec![tx1, tx2], + }; + let serialised = b.to_vec(); + assert_eq!(serialised, vec![ + // header + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, + // transactions + 2, 1, 0, 0, + // tx1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0, + // tx2 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 42, 0, 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0 + ]); +} + +#[test] +fn deserialise_block_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx1 = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 69, + function: Function::StakingTransfer, + input_data: Vec::new().join(&two).join(&69u64), + }, + signature: [1u8; 64], + }; + let tx2 = UncheckedTransaction { + transaction: Transaction { + signed: two.clone(), + nonce: 42, + function: Function::StakingStake, + input_data: Vec::new(), + }, + signature: [2u8; 64], + }; + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let b = Block { + header: h, + transactions: vec![tx1, tx2], + }; + let data = [ + // header + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, + // transactions + 2, 1, 0, 0, + // tx1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 69, 0, 0, 0, 0, 0, 0, 0, + 2, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0, + // tx2 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 42, 0, 0, 0, 0, 0, 0, 0, + 0, + 0, 0, 0, 0 + ]; + let deserialised = Block::from_slice(&data).unwrap(); + assert_eq!(deserialised, b); +} diff --git a/substrate/wasm-runtime/polkadot/src/primitives/transaction.rs b/substrate/wasm-runtime/polkadot/src/primitives/transaction.rs new file mode 100644 index 0000000000..9c72e31d34 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/transaction.rs @@ -0,0 +1,67 @@ +// 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 . + +//! Transaction type. + +use runtime_std::prelude::*; +use codec::{StreamReader, Joiner, Slicable, NonTrivialSlicable}; +use primitives::{AccountID, TxOrder, Function}; +use runtime_std::mem; + +/// A vetted and verified transaction from the external world. +#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))] +pub struct Transaction { + /// Who signed it (note this is not a signature). + pub signed: AccountID, + /// The number of transactions have come before from the same signer. + pub nonce: TxOrder, + /// The function that should be called. + pub function: Function, + /// Serialised input data to the function. + pub input_data: Vec, +} + +impl Slicable for Transaction { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Transaction { + signed: reader.read()?, + nonce: reader.read()?, + function: Function::from_u8(reader.read()?)?, + input_data: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: &F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.signed) + .join(&self.nonce) + .join(&(self.function as u8)) + .join(&self.input_data) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = mem::size_of::() + mem::size_of::() + mem::size_of::(); + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for Transaction {} diff --git a/substrate/wasm-runtime/polkadot/src/primitives/uncheckedtransaction.rs b/substrate/wasm-runtime/polkadot/src/primitives/uncheckedtransaction.rs new file mode 100644 index 0000000000..ec14befb55 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives/uncheckedtransaction.rs @@ -0,0 +1,83 @@ +// 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 . + +//! Unchecked Transaction type. + +use runtime_std::{mem, ed25519_verify}; +use runtime_std::prelude::*; +use codec::{Slicable, NonTrivialSlicable, StreamReader, Joiner}; +use primitives::Transaction; + +#[cfg(feature = "with-std")] +use std::fmt; + +/// A transactions right from the external world. Unchecked. +pub struct UncheckedTransaction { + /// The actual transaction information. + pub transaction: Transaction, + /// The signature; should be an Ed25519 signature applied to the serialised `transaction` field. + pub signature: [u8; 64], +} + +impl UncheckedTransaction { + /// Verify the signature. + pub fn ed25519_verify(&self) -> bool { + let msg = self.transaction.to_vec(); + ed25519_verify(&self.signature, &msg, &self.transaction.signed) + } +} + +#[cfg(feature = "with-std")] +impl PartialEq for UncheckedTransaction { + fn eq(&self, other: &Self) -> bool { + self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction + } +} + +#[cfg(feature = "with-std")] +impl fmt::Debug for UncheckedTransaction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UncheckedTransaction({:?})", self.transaction) + } +} + +impl Slicable for UncheckedTransaction { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(UncheckedTransaction { + signature: reader.read()?, + transaction: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: &F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + Vec::new() + .join(&self.signature) + .join(&self.transaction) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = mem::size_of::<[u8; 64]>(); + let second_part = ::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for UncheckedTransaction {} diff --git a/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs b/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs index 689fdca21e..51666666cf 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/consensus.rs @@ -16,8 +16,8 @@ //! Conensus module for runtime; manages the authority set ready for the native code. -use runtime_support::prelude::*; -use storable::StorageVec; +use runtime_std::prelude::*; +use support::StorageVec; use primitives::SessionKey; struct AuthorityStorageVec {} @@ -31,14 +31,18 @@ pub fn authorities() -> Vec { AuthorityStorageVec::items() } -/// Set the current set of authorities' session keys. -/// -/// Called by `next_session` only. -pub fn set_authorities(authorities: &[SessionKey]) { - AuthorityStorageVec::set_items(authorities); -} +pub mod internal { + use super::*; -/// Set a single authority by index. -pub fn set_authority(index: u32, key: &SessionKey) { - AuthorityStorageVec::set_item(index, key); + /// Set the current set of authorities' session keys. + /// + /// Called by `next_session` only. + pub fn set_authorities(authorities: &[SessionKey]) { + AuthorityStorageVec::set_items(authorities); + } + + /// Set a single authority by index. + pub fn set_authority(index: u32, key: &SessionKey) { + AuthorityStorageVec::set_item(index, key); + } } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs new file mode 100644 index 0000000000..4f7f6d8688 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs @@ -0,0 +1,366 @@ +// 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 . + +//! Governance system: Handles administration and dispatch of sensitive operations including +//! setting new code, minting new tokens and changing parameters. +//! +//! For now this is limited to a simple qualified majority vote (whose parameter is retrieved from +//! storage) between validators. A single vote may be proposed per era, and at most one approval +//! vote may be cast by each validator. The tally is maintained through a simple tag in storage for +//! each validator that has approved. +//! +//! At the end of the era, all validators approvals are tallied and if there are sufficient to pass +//! the proposal then it is enacted. All items in storage concerning the proposal are reset. + +use runtime_std::prelude::*; +use codec::KeyedVec; +use support::storage; +use primitives::{AccountID, Hash, BlockNumber, Proposal}; +use runtime::{staking, system, session}; + +const APPROVALS_REQUIRED: &[u8] = b"gov:apr"; +const CURRENT_PROPOSAL: &[u8] = b"gov:pro"; +const APPROVAL_OF: &[u8] = b"gov:app:"; + +/// The proportion of validators required for a propsal to be approved measured as the number out +/// of 1000. +pub fn approval_ppm_required() -> u32 { + storage::get_or(APPROVALS_REQUIRED, 1000) +} + +/// The number of concrete validator approvals required for a proposal to pass. +pub fn approvals_required() -> u32 { + approval_ppm_required() * session::validator_count() as u32 / 1000 +} + +pub mod public { + use super::*; + + /// Propose a sensitive action to be taken. Any action that is enactable by `Proposal` is valid. + /// Proposal is by the `transactor` and will automatically count as an approval. Transactor must + /// be a current validator. It is illegal to propose when there is already a proposal in effect. + pub fn propose(validator: &AccountID, proposal: &Proposal) { + if storage::exists(CURRENT_PROPOSAL) { + panic!("there may only be one proposal per era."); + } + storage::put(CURRENT_PROPOSAL, proposal); + approve(validator, staking::current_era()); + } + + /// Approve the current era's proposal. Transactor must be a validator. This may not be done more + /// than once for any validator in an era. + pub fn approve(validator: &AccountID, era_index: BlockNumber) { + if era_index != staking::current_era() { + panic!("approval vote applied on non-current era.") + } + if !storage::exists(CURRENT_PROPOSAL) { + panic!("there must be a proposal in order to approve."); + } + if session::validators().into_iter().position(|v| &v == validator).is_none() { + panic!("transactor must be a validator to approve."); + } + let key = validator.to_keyed_vec(APPROVAL_OF); + if storage::exists(&key) { + panic!("transactor may not approve a proposal twice in one era."); + } + storage::put(&key, &true); + } +} + +pub mod privileged { + use super::*; + + /// Set the proportion of validators that must approve for a proposal to be enacted at the end of + /// its era. The value, `ppm`, is measured as a fraction of 1000 rounded down to the nearest whole + /// validator. `1000` would require the approval of all validators; `667` would require two-thirds + /// (or there abouts) of validators. + pub fn set_approval_ppm_required(ppm: u32) { + storage::put(APPROVALS_REQUIRED, &ppm); + } +} + +pub mod internal { + use super::*; + + /// Current era is ending; we should finish up any proposals. + pub fn end_of_an_era() { + // tally up votes for the current proposal, if any. enact if there are sufficient approvals. + if let Some(proposal) = storage::take::(CURRENT_PROPOSAL) { + let approvals_required = approvals_required(); + let approved = session::validators().into_iter() + .filter_map(|v| storage::take::(&v.to_keyed_vec(APPROVAL_OF))) + .take(approvals_required as usize) + .count() as u32; + if approved == approvals_required { + proposal.enact(); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_std::{with_externalities, twox_128}; + use codec::{KeyedVec, Joiner}; + use support::{one, two, TestExternalities, with_env}; + use primitives::{AccountID, InternalFunction}; + use runtime::{staking, session}; + + fn new_test_ext() -> TestExternalities { + let one = one(); + let two = two(); + let three = [3u8; 32]; + + TestExternalities { storage: map![ + twox_128(APPROVALS_REQUIRED).to_vec() => vec![].join(&667u32), + twox_128(b"ses:len").to_vec() => vec![].join(&1u64), + 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(&1u64), + twox_128(b"sta:vac").to_vec() => vec![].join(&3u64), + twox_128(b"sta:era").to_vec() => vec![].join(&1u64) + ], } + } + + #[test] + fn majority_voting_should_work() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Approve it. Era length changes. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + public::approve(&two, 1); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 2); + }); + } + + #[test] + fn majority_voting_should_work_after_unsuccessful_previous() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Fail it. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + + // Block 2: Make proposal. Approve it. It should change era length. + with_env(|e| e.block_number = 2); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + public::approve(&two, 2); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 2); + }); + } + + #[test] + fn minority_voting_should_not_succeed() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Will have only 1 vote. No change. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + }); + } + + #[test] + #[should_panic] + fn old_voting_should_be_illegal() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Will have only 1 vote. No change. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + public::approve(&two, 0); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + }); + } + + #[test] + #[should_panic] + fn double_voting_should_be_illegal() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Will have only 1 vote. No change. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + public::approve(&two, 1); + public::approve(&two, 1); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + }); + } + + #[test] + #[should_panic] + fn over_proposing_should_be_illegal() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Will have only 1 vote. No change. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + public::propose(&two, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + }); + } + + #[test] + #[should_panic] + fn approving_without_proposal_should_be_illegal() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Will have only 1 vote. No change. + with_env(|e| e.block_number = 1); + public::approve(&two, 1); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + }); + } + + #[test] + #[should_panic] + fn non_validator_approving_should_be_illegal() { + let one = one(); + let two = two(); + let three = [3u8; 32]; + let four = [4u8; 32]; + let mut t = new_test_ext(); + + with_externalities(&mut t, || { + assert_eq!(staking::era_length(), 1u64); + assert_eq!(staking::current_era(), 1u64); + assert_eq!(session::validator_count(), 3usize); + assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]); + assert!(!session::validators().into_iter().position(|v| &v == &one).is_none()); + + // Block 1: Make proposal. Will have only 1 vote. No change. + with_env(|e| e.block_number = 1); + public::propose(&one, &Proposal { + function: InternalFunction::StakingSetSessionsPerEra, + input_data: vec![].join(&2u64), + }); + public::approve(&four, 1); + staking::internal::check_new_era(); + assert_eq!(staking::era_length(), 1); + }); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/runtime/mod.rs b/substrate/wasm-runtime/polkadot/src/runtime/mod.rs index f83922c42f..9befa9bd42 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/mod.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/mod.rs @@ -26,5 +26,8 @@ pub mod staking; pub mod timestamp; #[allow(unused)] pub mod session; +#[allow(unused)] +pub mod governance; -// TODO: governance, polkadao +// TODO: polkadao +// TODO: parachains diff --git a/substrate/wasm-runtime/polkadot/src/runtime/session.rs b/substrate/wasm-runtime/polkadot/src/runtime/session.rs index 6914bdc655..0402816fdc 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/session.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/session.rs @@ -17,119 +17,134 @@ //! Session manager: is told the validators and allows them to manage their session keys for the //! consensus module. -use runtime_support::prelude::*; -use keyedvec::KeyedVec; -use storable::{kill, Storable, StorageVec}; +use runtime_std::prelude::*; +use codec::KeyedVec; +use support::{storage, StorageVec}; use primitives::{AccountID, SessionKey, BlockNumber}; use runtime::{system, staking, consensus}; +const SESSION_LENGTH: &[u8] = b"ses:len"; +const CURRENT_INDEX: &[u8] = b"ses:ind"; +const LAST_LENGTH_CHANGE: &[u8] = b"ses:llc"; +const NEXT_KEY_FOR: &[u8] = b"ses:nxt:"; +const NEXT_SESSION_LENGTH: &[u8] = b"ses:nln"; + struct ValidatorStorageVec {} impl StorageVec for ValidatorStorageVec { type Item = AccountID; const PREFIX: &'static[u8] = b"ses:val:"; } -// TRANSACTION API (available to all transactors) - -/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next -/// session. -pub fn set_key(validator: &AccountID, key: &SessionKey) { - // set new value for next session - key.store(&validator.to_keyed_vec(b"ses:nxt:")); -} - -// PUBLIC API (available to other runtime modules) - /// Get the current set of authorities. These are the session keys. pub fn validators() -> Vec { ValidatorStorageVec::items() } -/// Set the current set of validators. -/// -/// Called by staking::next_era() only. `next_session` should be called after this in order to -/// update the session keys to the next validator set. -pub fn set_validators(new: &[AccountID]) { - ValidatorStorageVec::set_items(new); - consensus::set_authorities(new); -} - /// The number of blocks in each session. pub fn length() -> BlockNumber { - Storable::lookup_default(b"ses:len") + storage::get_or(SESSION_LENGTH, 0) +} + +/// The number of validators currently. +pub fn validator_count() -> usize { + ValidatorStorageVec::count() as usize } /// The current era index. pub fn current_index() -> BlockNumber { - Storable::lookup_default(b"ses:ind") -} - -/// Set the current era index. -pub fn set_current_index(new: BlockNumber) { - new.store(b"ses:ind"); + storage::get_or(CURRENT_INDEX, 0) } /// The block number at which the era length last changed. pub fn last_length_change() -> BlockNumber { - Storable::lookup_default(b"ses:llc") + storage::get_or(LAST_LENGTH_CHANGE, 0) } -/// Set a new era length. Won't kick in until the next era change (at current length). -pub fn set_length(new: BlockNumber) { - new.store(b"ses:nln"); -} +pub mod public { + use super::*; -/// Hook to be called after transaction processing. -pub fn check_rotate_session() { - // do this last, after the staking system has had chance to switch out the authorities for the - // new set. - // check block number and call next_session if necessary. - if (system::block_number() - last_length_change()) % length() == 0 { - rotate_session(); + /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next + /// session. + pub fn set_key(validator: &AccountID, key: &SessionKey) { + // set new value for next session + storage::put(&validator.to_keyed_vec(NEXT_KEY_FOR), key); } } -// PRIVATE (not available for use externally) +pub mod privileged { + use super::*; + + /// Set a new era length. Won't kick in until the next era change (at current length). + pub fn set_length(new: BlockNumber) { + storage::put(NEXT_SESSION_LENGTH, &new); + } +} + +// INTERNAL API (available to other runtime modules) + +pub mod internal { + use super::*; + + /// Set the current set of validators. + /// + /// Called by staking::next_era() only. `next_session` should be called after this in order to + /// update the session keys to the next validator set. + pub fn set_validators(new: &[AccountID]) { + ValidatorStorageVec::set_items(new); + consensus::internal::set_authorities(new); + } + + /// Hook to be called after transaction processing. + pub fn check_rotate_session() { + // do this last, after the staking system has had chance to switch out the authorities for the + // new set. + // check block number and call next_session if necessary. + if (system::block_number() - last_length_change()) % length() == 0 { + rotate_session(); + } + } +} /// Move onto next session: register the new authority set. fn rotate_session() { // Increment current session index. - set_current_index(current_index() + 1); + storage::put(CURRENT_INDEX, &(current_index() + 1)); // Enact era length change. - if let Some(next_len) = u64::lookup(b"ses:nln") { - next_len.store(b"ses:len"); - system::block_number().store(b"ses:llc"); - kill(b"ses:nln"); + if let Some(next_len) = storage::get::(NEXT_SESSION_LENGTH) { + storage::put(SESSION_LENGTH, &next_len); + storage::put(LAST_LENGTH_CHANGE, &system::block_number()); + storage::kill(NEXT_SESSION_LENGTH); } // Update any changes in session keys. validators().iter().enumerate().for_each(|(i, v)| { - let k = v.to_keyed_vec(b"ses:nxt:"); - if let Some(n) = Storable::lookup(&k) { - consensus::set_authority(i as u32, &n); - kill(&k); + let k = v.to_keyed_vec(NEXT_KEY_FOR); + if let Some(n) = storage::take(&k) { + consensus::internal::set_authority(i as u32, &n); } }); } #[cfg(test)] mod tests { - use runtime_support::{with_externalities, twox_128}; - use keyedvec::KeyedVec; - use joiner::Joiner; - use testing::{one, two, TestExternalities}; + use super::*; + use super::public::*; + use super::privileged::*; + use super::internal::*; + use runtime_std::{with_externalities, twox_128}; + use codec::{KeyedVec, Joiner}; + use support::{one, two, TestExternalities, with_env}; use primitives::AccountID; use runtime::{consensus, session}; - use environment::with_env; fn simple_setup() -> TestExternalities { TestExternalities { storage: map![ - twox_128(b"ses:len").to_vec() => vec![].join(&2u64), + twox_128(SESSION_LENGTH).to_vec() => vec![].join(&2u64), // the validators (10, 20, ...) twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32), - twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32], - twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32], + twox_128(&0u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![10; 32], + twox_128(&1u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![20; 32], // initial session keys (11, 21, ...) twox_128(b"con:aut:len").to_vec() => vec![].join(&2u32), twox_128(&0u32.to_keyed_vec(b"con:aut:")).to_vec() => vec![11; 32], @@ -142,8 +157,8 @@ mod tests { let mut t = simple_setup(); with_externalities(&mut t, || { assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); - assert_eq!(session::length(), 2u64); - assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); + assert_eq!(length(), 2u64); + assert_eq!(validators(), vec![[10u8; 32], [20u8; 32]]); }); } @@ -153,48 +168,48 @@ mod tests { with_externalities(&mut t, || { // Block 1: Change to length 3; no visible change. with_env(|e| e.block_number = 1); - session::set_length(3); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 0); + set_length(3); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 0); // Block 2: Length now changed to 3. Index incremented. with_env(|e| e.block_number = 2); - session::set_length(3); - session::check_rotate_session(); - assert_eq!(session::length(), 3); - assert_eq!(session::current_index(), 1); + set_length(3); + check_rotate_session(); + assert_eq!(length(), 3); + assert_eq!(current_index(), 1); // Block 3: Length now changed to 3. Index incremented. with_env(|e| e.block_number = 3); - session::check_rotate_session(); - assert_eq!(session::length(), 3); - assert_eq!(session::current_index(), 1); + check_rotate_session(); + assert_eq!(length(), 3); + assert_eq!(current_index(), 1); // Block 4: Change to length 2; no visible change. with_env(|e| e.block_number = 4); - session::set_length(2); - session::check_rotate_session(); - assert_eq!(session::length(), 3); - assert_eq!(session::current_index(), 1); + set_length(2); + check_rotate_session(); + assert_eq!(length(), 3); + assert_eq!(current_index(), 1); // Block 5: Length now changed to 2. Index incremented. with_env(|e| e.block_number = 5); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 2); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 2); // Block 6: No change. with_env(|e| e.block_number = 6); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 2); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 2); // Block 7: Next index. with_env(|e| e.block_number = 7); - session::check_rotate_session(); - assert_eq!(session::length(), 2); - assert_eq!(session::current_index(), 3); + check_rotate_session(); + assert_eq!(length(), 2); + assert_eq!(current_index(), 3); }); } @@ -204,25 +219,25 @@ mod tests { with_externalities(&mut t, || { // Block 1: No change with_env(|e| e.block_number = 1); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); // Block 2: Session rollover, but no change. with_env(|e| e.block_number = 2); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); // Block 3: Set new key for validator 2; no visible change. with_env(|e| e.block_number = 3); - session::set_key(&[20; 32], &[22; 32]); + set_key(&[20; 32], &[22; 32]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); // Block 4: Session rollover, authority 2 changes. with_env(|e| e.block_number = 4); - session::check_rotate_session(); + check_rotate_session(); assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]); }); } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/staking.rs b/substrate/wasm-runtime/polkadot/src/runtime/staking.rs index 9b0c659f5c..d9bb971647 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/staking.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/staking.rs @@ -16,11 +16,12 @@ //! Staking manager: Handles balances and periodically determines the best set of validators. -use runtime_support::prelude::*; -use keyedvec::KeyedVec; -use storable::{Storable, StorageVec}; +use runtime_std::prelude::*; +use runtime_std::cell::RefCell; +use codec::KeyedVec; +use support::{storage, StorageVec}; use primitives::{BlockNumber, AccountID}; -use runtime::{system, session}; +use runtime::{system, session, governance}; /// The balance of an account. pub type Balance = u64; @@ -31,23 +32,26 @@ pub type Bondage = u64; struct IntentionStorageVec {} impl StorageVec for IntentionStorageVec { type Item = AccountID; - const PREFIX: &'static[u8] = b"ses:wil:"; + const PREFIX: &'static[u8] = b"sta:wil:"; } -// Each identity's stake may be in one of three bondage states, given by an integer: -// - n | n <= current_era(): inactive: free to be transferred. -// - ~0: active: currently representing a validator. -// - n | n > current_era(): deactivating: recently representing a validator and not yet -// ready for transfer. +const BONDING_DURATION: &[u8] = b"sta:loc"; +const VALIDATOR_COUNT: &[u8] = b"sta:vac"; +const SESSIONS_PER_ERA: &[u8] = b"sta:spe"; +const NEXT_SESSIONS_PER_ERA: &[u8] = b"sta:nse"; +const CURRENT_ERA: &[u8] = b"sta:era"; +const LAST_ERA_LENGTH_CHANGE: &[u8] = b"sta:lec"; +const BALANCE_OF: &[u8] = b"sta:bal:"; +const BONDAGE_OF: &[u8] = b"sta:bon:"; /// The length of the bonding duration in eras. pub fn bonding_duration() -> BlockNumber { - Storable::lookup_default(b"sta:loc") + storage::get_or_default(BONDING_DURATION) } /// The length of a staking era in sessions. pub fn validator_count() -> usize { - u32::lookup_default(b"sta:vac") as usize + storage::get_or_default::(VALIDATOR_COUNT) as usize } /// The length of a staking era in blocks. @@ -57,95 +61,129 @@ pub fn era_length() -> BlockNumber { /// The length of a staking era in sessions. pub fn sessions_per_era() -> BlockNumber { - Storable::lookup_default(b"sta:spe") + storage::get_or_default(SESSIONS_PER_ERA) } /// The current era index. pub fn current_era() -> BlockNumber { - Storable::lookup_default(b"sta:era") + storage::get_or_default(CURRENT_ERA) } /// The block number at which the era length last changed. pub fn last_era_length_change() -> BlockNumber { - Storable::lookup_default(b"sta:lec") + storage::get_or_default(LAST_ERA_LENGTH_CHANGE) } /// The balance of a given account. pub fn balance(who: &AccountID) -> Balance { - Storable::lookup_default(&who.to_keyed_vec(b"sta:bal:")) + storage::get_or_default(&who.to_keyed_vec(BALANCE_OF)) } /// The liquidity-state of a given account. pub fn bondage(who: &AccountID) -> Bondage { - Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:")) + storage::get_or_default(&who.to_keyed_vec(BONDAGE_OF)) } -/// Transfer some unlocked staking balance to another staker. -pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { - let from_key = transactor.to_keyed_vec(b"sta:bal:"); - let from_balance = Balance::lookup_default(&from_key); - assert!(from_balance >= value); - let to_key = dest.to_keyed_vec(b"sta:bal:"); - let to_balance: Balance = Storable::lookup_default(&to_key); - assert!(bondage(transactor) <= bondage(dest)); - assert!(to_balance + value > to_balance); // no overflow - (from_balance - value).store(&from_key); - (to_balance + value).store(&to_key); -} +// Each identity's stake may be in one of three bondage states, given by an integer: +// - n | n <= current_era(): inactive: free to be transferred. +// - ~0: active: currently representing a validator. +// - n | n > current_era(): deactivating: recently representing a validator and not yet +// ready for transfer. -/// Declare the desire to stake for the transactor. -/// -/// Effects will be felt at the beginning of the next era. -pub fn stake(transactor: &AccountID) { - let mut intentions = IntentionStorageVec::items(); - // can't be in the list twice. - assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked."); - intentions.push(transactor.clone()); - IntentionStorageVec::set_items(&intentions); - u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:")); -} +pub mod public { + use super::*; -/// Retract the desire to stake for the transactor. -/// -/// Effects will be felt at the beginning of the next era. -pub fn unstake(transactor: &AccountID) { - let mut intentions = IntentionStorageVec::items(); - if let Some(position) = intentions.iter().position(|t| t == transactor) { - intentions.swap_remove(position); - } else { - panic!("Cannot unstake if not already staked."); + /// Transfer some unlocked staking balance to another staker. + pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { + let from_key = transactor.to_keyed_vec(BALANCE_OF); + let from_balance = storage::get_or_default::(&from_key); + assert!(from_balance >= value); + let to_key = dest.to_keyed_vec(BALANCE_OF); + let to_balance: Balance = storage::get_or_default(&to_key); + assert!(bondage(transactor) <= bondage(dest)); + assert!(to_balance + value > to_balance); // no overflow + storage::put(&from_key, &(from_balance - value)); + storage::put(&to_key, &(to_balance + value)); } - IntentionStorageVec::set_items(&intentions); - (current_era() + bonding_duration()).store(&transactor.to_keyed_vec(b"sta:bon:")); -} -/// Hook to be called after to transaction processing. -pub fn check_new_era() { - // check block number and call new_era if necessary. - if (system::block_number() - last_era_length_change()) % era_length() == 0 { - new_era(); + /// Declare the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + pub fn stake(transactor: &AccountID) { + let mut intentions = IntentionStorageVec::items(); + // can't be in the list twice. + assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked."); + intentions.push(transactor.clone()); + IntentionStorageVec::set_items(&intentions); + storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &u64::max_value()); + } + + /// Retract the desire to stake for the transactor. + /// + /// Effects will be felt at the beginning of the next era. + pub fn unstake(transactor: &AccountID) { + let mut intentions = IntentionStorageVec::items(); + if let Some(position) = intentions.iter().position(|t| t == transactor) { + intentions.swap_remove(position); + } else { + panic!("Cannot unstake if not already staked."); + } + IntentionStorageVec::set_items(&intentions); + storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &(current_era() + bonding_duration())); } } -// PRIVATE +pub mod privileged { + use super::*; + + /// Set the number of sessions in an era. + pub fn set_sessions_per_era(new: BlockNumber) { + storage::put(NEXT_SESSIONS_PER_ERA, &new); + } + + /// The length of the bonding duration in eras. + pub fn set_bonding_duration(new: BlockNumber) { + storage::put(BONDING_DURATION, &new); + } + + /// The length of a staking era in sessions. + pub fn set_validator_count(new: usize) { + storage::put(VALIDATOR_COUNT, &(new as u32)); + } +} + +pub mod internal { + use super::*; + + /// Hook to be called after to transaction processing. + pub fn check_new_era() { + // check block number and call new_era if necessary. + if (system::block_number() - last_era_length_change()) % era_length() == 0 { + new_era(); + } + } +} /// The era has changed - enact new staking set. /// -/// NOTE: This always happens on a session change. +/// NOTE: This always happens immediately before a session change to ensure that new validators +/// get a chance to set their session keys. fn new_era() { + // Inform governance module that it's the end of an era + governance::internal::end_of_an_era(); + // Increment current era. - (current_era() + 1).store(b"sta:era"); + storage::put(CURRENT_ERA, &(current_era() + 1)); // Enact era length change. - let next_spe: u64 = Storable::lookup_default(b"sta:nse"); + let next_spe: u64 = storage::get_or_default(NEXT_SESSIONS_PER_ERA); if next_spe > 0 && next_spe != sessions_per_era() { - next_spe.store(b"sta:spe"); - system::block_number().store(b"sta:lec"); + storage::put(SESSIONS_PER_ERA, &next_spe); + storage::put(LAST_ERA_LENGTH_CHANGE, &system::block_number()); } - // TODO: evaluate desired staking amounts and nominations and optimise to find the best - // combination of validators, then use session::set_validators(). - + // evaluate desired staking amounts and nominations and optimise to find the best + // combination of validators, then use session::internal::set_validators(). // for now, this just orders would-be stakers by their balances and chooses the top-most // validator_count() of them. let mut intentions = IntentionStorageVec::items() @@ -153,7 +191,7 @@ fn new_era() { .map(|v| (balance(&v), v)) .collect::>(); intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1)); - session::set_validators( + session::internal::set_validators( &intentions.into_iter() .map(|(_, v)| v) .take(validator_count()) @@ -161,20 +199,18 @@ fn new_era() { ); } -/// Set a new era length. Won't kick in until the next era change (at current length). -fn set_sessions_per_era(new: BlockNumber) { - new.store(b"sta:nse"); -} - #[cfg(test)] mod tests { - use runtime_support::{with_externalities, twox_128}; - use keyedvec::KeyedVec; - use joiner::Joiner; - use testing::{one, two, TestExternalities}; + use super::*; + use super::internal::*; + use super::public::*; + use super::privileged::*; + + use runtime_std::{with_externalities, twox_128}; + use codec::{KeyedVec, Joiner}; + use support::{one, two, TestExternalities, with_env}; use primitives::AccountID; use runtime::{staking, session}; - use environment::with_env; #[test] fn staking_should_work() { @@ -188,64 +224,64 @@ mod tests { twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32), twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32], twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32], - twox_128(b"sta:spe").to_vec() => vec![].join(&2u64), - twox_128(b"sta:vac").to_vec() => vec![].join(&2u32), - twox_128(b"sta:loc").to_vec() => vec![].join(&3u64), - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&10u64), - twox_128(&two.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&20u64), - twox_128(&three.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&30u64), - twox_128(&four.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&40u64) + twox_128(SESSIONS_PER_ERA).to_vec() => vec![].join(&2u64), + twox_128(VALIDATOR_COUNT).to_vec() => vec![].join(&2u32), + twox_128(BONDING_DURATION).to_vec() => vec![].join(&3u64), + twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&10u64), + twox_128(&two.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&20u64), + twox_128(&three.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&30u64), + twox_128(&four.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&40u64) ], }; with_externalities(&mut t, || { - assert_eq!(staking::era_length(), 2u64); - assert_eq!(staking::validator_count(), 2usize); - assert_eq!(staking::bonding_duration(), 3u64); + assert_eq!(era_length(), 2u64); + assert_eq!(validator_count(), 2usize); + assert_eq!(bonding_duration(), 3u64); assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); // Block 1: Add three validators. No obvious change. with_env(|e| e.block_number = 1); - staking::stake(&one); - staking::stake(&two); - staking::stake(&four); - staking::check_new_era(); + stake(&one); + stake(&two); + stake(&four); + check_new_era(); assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); // Block 2: New validator set now. with_env(|e| e.block_number = 2); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![four.clone(), two.clone()]); // Block 3: Unstake highest, introduce another staker. No change yet. with_env(|e| e.block_number = 3); - staking::stake(&three); - staking::unstake(&four); - staking::check_new_era(); + stake(&three); + unstake(&four); + check_new_era(); // Block 4: New era - validators change. with_env(|e| e.block_number = 4); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![three.clone(), two.clone()]); // Block 5: Transfer stake from highest to lowest. No change yet. with_env(|e| e.block_number = 5); - staking::transfer(&four, &one, 40); - staking::check_new_era(); + transfer(&four, &one, 40); + check_new_era(); // Block 6: Lowest now validator. with_env(|e| e.block_number = 6); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![one.clone(), three.clone()]); // Block 7: Unstake three. No change yet. with_env(|e| e.block_number = 7); - staking::unstake(&three); - staking::check_new_era(); + unstake(&three); + check_new_era(); assert_eq!(session::validators(), vec![one.clone(), three.clone()]); // Block 8: Back to one and two. with_env(|e| e.block_number = 8); - staking::check_new_era(); + check_new_era(); assert_eq!(session::validators(), vec![one.clone(), two.clone()]); }); } @@ -254,63 +290,63 @@ mod tests { fn staking_eras_work() { let mut t = TestExternalities { storage: map![ twox_128(b"ses:len").to_vec() => vec![].join(&1u64), - twox_128(b"sta:spe").to_vec() => vec![].join(&2u64) + twox_128(SESSIONS_PER_ERA).to_vec() => vec![].join(&2u64) ], }; with_externalities(&mut t, || { - assert_eq!(staking::era_length(), 2u64); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 0u64); + assert_eq!(era_length(), 2u64); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 0u64); // Block 1: No change. with_env(|e| e.block_number = 1); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 0u64); + check_new_era(); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 0u64); // Block 2: Simple era change. with_env(|e| e.block_number = 2); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 1u64); + check_new_era(); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 1u64); // Block 3: Schedule an era length change; no visible changes. with_env(|e| e.block_number = 3); - staking::set_sessions_per_era(3); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 2u64); - assert_eq!(staking::last_era_length_change(), 0u64); - assert_eq!(staking::current_era(), 1u64); + set_sessions_per_era(3); + check_new_era(); + assert_eq!(sessions_per_era(), 2u64); + assert_eq!(last_era_length_change(), 0u64); + assert_eq!(current_era(), 1u64); // Block 4: Era change kicks in. with_env(|e| e.block_number = 4); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 2u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 2u64); // Block 5: No change. with_env(|e| e.block_number = 5); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 2u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 2u64); // Block 6: No change. with_env(|e| e.block_number = 6); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 2u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 2u64); // Block 7: Era increment. with_env(|e| e.block_number = 7); - staking::check_new_era(); - assert_eq!(staking::sessions_per_era(), 3u64); - assert_eq!(staking::last_era_length_change(), 4u64); - assert_eq!(staking::current_era(), 3u64); + check_new_era(); + assert_eq!(sessions_per_era(), 3u64); + assert_eq!(last_era_length_change(), 4u64); + assert_eq!(current_era(), 3u64); }); } @@ -320,12 +356,12 @@ mod tests { let two = two(); let mut t = TestExternalities { storage: map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&42u64) + twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&42u64) ], }; with_externalities(&mut t, || { - assert_eq!(staking::balance(&one), 42); - assert_eq!(staking::balance(&two), 0); + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 0); }); } @@ -335,13 +371,13 @@ mod tests { let two = two(); let mut t = TestExternalities { storage: map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&111u64) + twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&111u64) ], }; with_externalities(&mut t, || { - staking::transfer(&one, &two, 69); - assert_eq!(staking::balance(&one), 42); - assert_eq!(staking::balance(&two), 69); + transfer(&one, &two, 69); + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); }); } @@ -352,12 +388,12 @@ mod tests { let two = two(); let mut t = TestExternalities { storage: map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&111u64) + twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&111u64) ], }; with_externalities(&mut t, || { - staking::stake(&one); - staking::transfer(&one, &two, 69); + stake(&one); + transfer(&one, &two, 69); }); } } diff --git a/substrate/wasm-runtime/polkadot/src/runtime/system.rs b/substrate/wasm-runtime/polkadot/src/runtime/system.rs index 072e17df01..a6bf24718e 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/system.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/system.rs @@ -17,14 +17,16 @@ //! System manager: Handles all of the top-level stuff; executing block/transaction, setting code //! and depositing logs. -use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder, Hashable}; -use runtime_support::mem; -use runtime_support::prelude::*; -use storable::Storable; -use keyedvec::KeyedVec; -use environment::with_env; +use runtime_std::prelude::*; +use runtime_std::{mem, print}; +use codec::KeyedVec; +use support::{Hashable, storage, with_env}; +use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder}; use runtime::{staking, session}; +const BLOCK_HASH_AT: &[u8] = b"sys:old:"; +const CODE: &[u8] = b"sys:cod"; + /// The current block number being processed. Set by `execute_block`. pub fn block_number() -> BlockNumber { with_env(|e| e.block_number) @@ -32,77 +34,86 @@ pub fn block_number() -> BlockNumber { /// Get the block hash of a given block (uses storage). pub fn block_hash(number: BlockNumber) -> Hash { - Storable::lookup_default(&number.to_keyed_vec(b"sys:old:")) + storage::get_or_default(&number.to_keyed_vec(BLOCK_HASH_AT)) } -/// Deposits a log and ensures it matches the blocks log data. -pub fn deposit_log(log: &[u8]) { - with_env(|e| { - assert_eq!(log, &e.digest.logs[e.next_log_index][..]); - e.next_log_index += 1; - }); +pub mod privileged { + use super::*; + + /// Set the new code. + pub fn set_code(new: &[u8]) { + storage::put_raw(CODE, new); + } } -pub fn execute_block(mut block: Block) { - // populate environment from header. - with_env(|e| { - e.block_number = block.header.number; - mem::swap(&mut e.digest, &mut block.header.digest); - e.next_log_index = 0; - }); +pub mod internal { + use super::*; - let ref header = block.header; + /// Deposits a log and ensures it matches the blocks log data. + pub fn deposit_log(log: &[u8]) { + with_env(|e| { + assert_eq!(log, &e.digest.logs[e.next_log_index][..]); + e.next_log_index += 1; + }); + } - // check parent_hash is correct. - assert!( - header.number > 0 && block_hash(header.number - 1) == header.parent_hash, - "Parent hash should be valid." - ); + /// Actually execute all transitioning for `block`. + pub fn execute_block(mut block: Block) { + // populate environment from header. + with_env(|e| { + e.block_number = block.header.number; + mem::swap(&mut e.digest, &mut block.header.digest); + e.next_log_index = 0; + }); - // 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. + let ref header = block.header; - // store the header hash in storage. - let header_hash_key = header.number.to_keyed_vec(b"sys:old:"); - header.blake2_256().store(&header_hash_key); + // check parent_hash is correct. + assert!( + header.number > 0 && block_hash(header.number - 1) == header.parent_hash, + "Parent hash should be valid." + ); - // execute transactions - block.transactions.iter().for_each(execute_transaction); + // 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. - staking::check_new_era(); - session::check_rotate_session(); + // 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()); - // any final checks - final_checks(&block); + // execute transactions + block.transactions.iter().for_each(execute_transaction); - // 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. -} + staking::internal::check_new_era(); + session::internal::check_rotate_session(); -/// Execute a given transaction. -pub fn execute_transaction(utx: &UncheckedTransaction) { - // Verify the signature is good. - assert!(utx.ed25519_verify(), "All transactions should be properly signed"); + // any final checks + final_checks(&block); - let ref tx = utx.transaction; + // 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 nonce - let nonce_key = tx.signed.to_keyed_vec(b"sys:non:"); - let expected_nonce: TxOrder = Storable::lookup_default(&nonce_key); - assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); + /// Execute a given transaction. + pub fn execute_transaction(utx: &UncheckedTransaction) { + // Verify the signature is good. + assert!(utx.ed25519_verify(), "All transactions should be properly signed"); - // increment nonce in storage - (expected_nonce + 1).store(&nonce_key); + let ref tx = utx.transaction; - // decode parameters and dispatch - tx.function.dispatch(&tx.signed, &tx.input_data); -} + // check nonce + let nonce_key = tx.signed.to_keyed_vec(b"sys:non:"); + let expected_nonce: TxOrder = storage::get_or_default(&nonce_key); + assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); -/// Set the new code. -pub fn set_code(new: &[u8]) { - new.store(b":code"); + // increment nonce in storage + storage::put(&nonce_key, &(expected_nonce + 1)); + + // decode parameters and dispatch + tx.function.dispatch(&tx.signed, &tx.input_data); + } } fn final_checks(_block: &Block) { @@ -113,15 +124,14 @@ fn final_checks(_block: &Block) { #[cfg(test)] mod tests { - use joiner::Joiner; - use function::Function; - use keyedvec::KeyedVec; - use slicable::Slicable; - use runtime_support::{with_externalities, twox_128}; - use primitives::{UncheckedTransaction, Transaction}; - use statichex::StaticHexInto; - use runtime::{system, staking}; - use testing::{TestExternalities, HexDisplay, one, two}; + use super::*; + use super::internal::*; + + use runtime_std::{with_externalities, twox_128}; + use codec::{Joiner, KeyedVec, Slicable}; + use support::{StaticHexInto, TestExternalities, HexDisplay, one, two}; + use primitives::{UncheckedTransaction, Transaction, Function}; + use runtime::staking; #[test] fn staking_balance_transfer_dispatch_works() { @@ -147,7 +157,7 @@ mod tests { println!("tx is {}", HexDisplay::from(&tx.transaction.to_vec())); with_externalities(&mut t, || { - system::execute_transaction(&tx); + execute_transaction(&tx); assert_eq!(staking::balance(&one), 42); assert_eq!(staking::balance(&two), 69); }); diff --git a/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs b/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs index 7040473b1a..f0a798d112 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/timestamp.rs @@ -16,39 +16,47 @@ //! Timestamp manager: just handles the current timestamp. -use storable::Storable; +use support::storage; + +const CURRENT_TIMESTAMP: &[u8] = b"tim:val"; /// Representation of a time. pub type Timestamp = u64; /// Get the current time. pub fn get() -> Timestamp { - Storable::lookup_default(b"tim:val") + storage::get_or_default(CURRENT_TIMESTAMP) } -/// Set the current time. -pub fn set(now: Timestamp) { - now.store(b"tim:val") +pub mod public { + use super::*; + + /// Set the current time. + pub fn set(now: Timestamp) { + storage::put(CURRENT_TIMESTAMP, &now); + } } #[cfg(test)] mod tests { - use joiner::Joiner; - use keyedvec::KeyedVec; - use runtime_support::{with_externalities, twox_128}; + use super::*; + use super::public::*; + + use runtime_std::{with_externalities, twox_128}; use runtime::timestamp; - use testing::TestExternalities; + use codec::{Joiner, KeyedVec}; + use support::TestExternalities; #[test] fn timestamp_works() { let mut t = TestExternalities { storage: map![ - twox_128(b"tim:val").to_vec() => vec![].join(&42u64) + twox_128(CURRENT_TIMESTAMP).to_vec() => vec![].join(&42u64) ], }; with_externalities(&mut t, || { - assert_eq!(timestamp::get(), 42); - timestamp::set(69); - assert_eq!(timestamp::get(), 69); + assert_eq!(get(), 42); + set(69); + assert_eq!(get(), 69); }); } } diff --git a/substrate/wasm-runtime/polkadot/src/support/environment.rs b/substrate/wasm-runtime/polkadot/src/support/environment.rs index 1011b3168b..7670a4801f 100644 --- a/substrate/wasm-runtime/polkadot/src/support/environment.rs +++ b/substrate/wasm-runtime/polkadot/src/support/environment.rs @@ -16,10 +16,10 @@ //! Environment API: Allows certain information to be accessed throughout the runtime. -use runtime_support::boxed::Box; -use runtime_support::mem; -use runtime_support::cell::RefCell; -use runtime_support::rc::Rc; +use runtime_std::boxed::Box; +use runtime_std::mem; +use runtime_std::cell::RefCell; +use runtime_std::rc::Rc; use primitives::{BlockNumber, Digest}; diff --git a/substrate/wasm-runtime/polkadot/src/support/hashable.rs b/substrate/wasm-runtime/polkadot/src/support/hashable.rs new file mode 100644 index 0000000000..66c3844fda --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/support/hashable.rs @@ -0,0 +1,38 @@ +// 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 . + +//! Hashable trait. + +use codec::Slicable; +use runtime_std::{blake2_256, twox_128, twox_256}; + +pub trait Hashable: Sized { + fn blake2_256(&self) -> [u8; 32]; + fn twox_128(&self) -> [u8; 16]; + fn twox_256(&self) -> [u8; 32]; +} + +impl Hashable for T { + fn blake2_256(&self) -> [u8; 32] { + blake2_256(&self.to_vec()) + } + fn twox_128(&self) -> [u8; 16] { + twox_128(&self.to_vec()) + } + fn twox_256(&self) -> [u8; 32] { + twox_256(&self.to_vec()) + } +} diff --git a/substrate/wasm-runtime/polkadot/src/support/mod.rs b/substrate/wasm-runtime/polkadot/src/support/mod.rs index 56ad3a355c..6bc60880a0 100644 --- a/substrate/wasm-runtime/polkadot/src/support/mod.rs +++ b/substrate/wasm-runtime/polkadot/src/support/mod.rs @@ -16,13 +16,19 @@ //! Support code for the runtime. -pub mod primitives; -pub mod function; -pub mod environment; -pub mod storable; - -#[cfg(test)] -pub mod statichex; -#[cfg(test)] +mod environment; +pub mod storage; +mod hashable; +#[cfg(feature = "with-std")] +mod statichex; #[macro_use] -pub mod testing; +#[cfg(feature = "with-std")] +mod testing; + +pub use self::environment::with_env; +pub use self::storage::StorageVec; +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}; diff --git a/substrate/wasm-runtime/polkadot/src/support/primitives.rs b/substrate/wasm-runtime/polkadot/src/support/primitives.rs deleted file mode 100644 index 338be80710..0000000000 --- a/substrate/wasm-runtime/polkadot/src/support/primitives.rs +++ /dev/null @@ -1,515 +0,0 @@ -// 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 . - -//! Primitive types. - -use runtime_support::prelude::*; -use streamreader::StreamReader; -use joiner::Joiner; -use slicable::{Slicable, NonTrivialSlicable}; -use function::Function; -use runtime_support::{mem, blake2_256, twox_128, twox_256, ed25519_verify}; - -#[cfg(test)] -use std::fmt; - -/// The Ed25519 pubkey that identifies an account. -pub type AccountID = [u8; 32]; -/// The Ed25519 pub key of an session that belongs to an authority. This is used as what the -/// external environment/consensus algorithm calls an "authority". -pub type SessionKey = AccountID; -/// Indentifier for a chain. -pub type ChainID = u64; -/// Index of a block in the chain. -pub type BlockNumber = u64; -/// Index of a transaction. -pub type TxOrder = u64; -/// A hash of some data. -pub type Hash = [u8; 32]; - -#[derive(Clone, Default)] -#[cfg_attr(test, derive(PartialEq, Debug))] -/// The digest of a block, useful for light-clients. -pub struct Digest { - /// All logs that have happened in the block. - pub logs: Vec>, -} - -#[derive(Clone)] -#[cfg_attr(test, derive(PartialEq, Debug))] -/// The header for a block. -pub struct Header { - /// The parent block's "hash" (actually the Blake2-256 hash of its serialised header). - pub parent_hash: Hash, - /// The block's number (how many ancestors does it have?). - pub number: BlockNumber, - /// The root of the trie that represents this block's final storage map. - pub state_root: Hash, - /// The root of the trie that represents this block's transactions, indexed by a 32-bit integer. - pub transaction_root: Hash, - /// The digest for this block. - pub digest: Digest, -} - -impl Slicable for Header { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(Header { - parent_hash: reader.read()?, - number: reader.read()?, - state_root: reader.read()?, - transaction_root: reader.read()?, - digest: Digest { logs: reader.read()?, }, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - Vec::new() - .join(&self.parent_hash) - .join(&self.number) - .join(&self.state_root) - .join(&self.transaction_root) - .join(&self.digest.logs) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = mem::size_of::() + mem::size_of::() + mem::size_of::() + mem::size_of::(); - let second_part = >>::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -impl NonTrivialSlicable for Header {} - -#[cfg_attr(test, derive(PartialEq, Debug))] -/// A vetted and verified transaction from the external world. -pub struct Transaction { - /// Who signed it (note this is not a signature). - pub signed: AccountID, - /// The number of transactions have come before from the same signer. - pub nonce: TxOrder, - /// The function that should be called. - pub function: Function, - /// Serialised input data to the function. - pub input_data: Vec, -} - -impl Slicable for Transaction { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(Transaction { - signed: reader.read()?, - nonce: reader.read()?, - function: Function::from_u8(reader.read()?)?, - input_data: reader.read()?, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - Vec::new() - .join(&self.signed) - .join(&self.nonce) - .join(&(self.function as u8)) - .join(&self.input_data) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = mem::size_of::() + mem::size_of::() + mem::size_of::(); - let second_part = >::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -pub trait Hashable: Sized { - fn blake2_256(&self) -> [u8; 32]; - fn twox_128(&self) -> [u8; 16]; - fn twox_256(&self) -> [u8; 32]; -} - -impl Hashable for T { - fn blake2_256(&self) -> [u8; 32] { - blake2_256(&self.to_vec()) - } - fn twox_128(&self) -> [u8; 16] { - twox_128(&self.to_vec()) - } - fn twox_256(&self) -> [u8; 32] { - twox_256(&self.to_vec()) - } -} - -impl NonTrivialSlicable for Transaction {} - -/// A transactions right from the external world. Unchecked. -pub struct UncheckedTransaction { - /// The actual transaction information. - pub transaction: Transaction, - /// The signature; should be an Ed25519 signature applied to the serialised `transaction` field. - pub signature: [u8; 64], -} - -impl UncheckedTransaction { - /// Verify the signature. - pub fn ed25519_verify(&self) -> bool { - let msg = self.transaction.to_vec(); - ed25519_verify(&self.signature, &msg, &self.transaction.signed) - } -} - -#[cfg(test)] -impl PartialEq for UncheckedTransaction { - fn eq(&self, other: &Self) -> bool { - self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction - } -} - -#[cfg(test)] -impl fmt::Debug for UncheckedTransaction { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UncheckedTransaction({:?})", self.transaction) - } -} - -impl Slicable for UncheckedTransaction { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(UncheckedTransaction { - signature: reader.read()?, - transaction: reader.read()?, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - Vec::new() - .join(&self.signature) - .join(&self.transaction) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = mem::size_of::<[u8; 64]>(); - let second_part = ::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -impl NonTrivialSlicable for UncheckedTransaction {} - -#[cfg_attr(test, derive(PartialEq, Debug))] -/// A Polkadot relay chain block. -pub struct Block { - /// The header of the block. - pub header: Header, - /// All transactions. - pub transactions: Vec, -} - -impl Slicable for Block { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(Block { - header: reader.read()?, - transactions: reader.read()?, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - Vec::new() - .join(&self.header) - .join(&self.transactions) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = Header::size_of(data)?; - let second_part = >::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -impl NonTrivialSlicable for Block {} - -impl NonTrivialSlicable for Vec where Vec: Slicable {} - -impl Slicable for Vec { - fn from_slice(value: &[u8]) -> Option { - let len = Self::size_of(&value[0..4])?; - let mut off = 4; - let mut r = Vec::new(); - while off < len { - let element_len = T::size_of(&value[off..])?; - r.push(T::from_slice(&value[off..off + element_len])?); - off += element_len; - } - Some(r) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - let vecs = self.iter().map(Slicable::to_vec).collect::>(); - let len = vecs.iter().fold(0, |mut a, v| {a += v.len(); a}); - let mut r = Vec::new().join(&(len as u32)); - vecs.iter().for_each(|v| r.extend_from_slice(v)); - r - } - - fn size_of(data: &[u8]) -> Option { - u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use joiner::Joiner; - use function::Function; - - #[test] - fn serialise_transaction_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx = Transaction { - signed: one.clone(), - nonce: 69, - function: Function::StakingTransfer, - input_data: Vec::new().join(&two).join(&69u64), - }; - let serialised = tx.to_vec(); - assert_eq!(serialised, vec![ - 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 69, 0, 0, 0, 0, 0, 0, 0, - 2, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0 - ]); - } - - #[test] - fn deserialise_transaction_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx = Transaction { - signed: one.clone(), - nonce: 69, - function: Function::StakingTransfer, - input_data: Vec::new().join(&two).join(&69u64), - }; - let data = [ - 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 69, 0, 0, 0, 0, 0, 0, 0, - 2, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0 - ]; - let deserialised = Transaction::from_slice(&data).unwrap(); - assert_eq!(deserialised, tx); - } - - #[test] - fn serialise_header_works() { - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let serialised = h.to_vec(); - assert_eq!(serialised, vec![ - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 - ]); - } - - #[test] - fn deserialise_header_works() { - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let data = [ - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 - ]; - let deserialised = Header::from_slice(&data).unwrap(); - assert_eq!(deserialised, h); - } - - #[test] - fn serialise_block_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx1 = UncheckedTransaction { - transaction: Transaction { - signed: one.clone(), - nonce: 69, - function: Function::StakingTransfer, - input_data: Vec::new().join(&two).join(&69u64), - }, - signature: [1u8; 64], - }; - let tx2 = UncheckedTransaction { - transaction: Transaction { - signed: two.clone(), - nonce: 42, - function: Function::StakingStake, - input_data: Vec::new(), - }, - signature: [2u8; 64], - }; - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let b = Block { - header: h, - transactions: vec![tx1, tx2], - }; - let serialised = b.to_vec(); - assert_eq!(serialised, vec![ - // header - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, - // transactions - 2, 1, 0, 0, - // tx1 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 69, 0, 0, 0, 0, 0, 0, 0, - 2, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0, - // tx2 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 42, 0, 0, 0, 0, 0, 0, 0, - 0, - 0, 0, 0, 0 - ]); - } - - #[test] - fn deserialise_block_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx1 = UncheckedTransaction { - transaction: Transaction { - signed: one.clone(), - nonce: 69, - function: Function::StakingTransfer, - input_data: Vec::new().join(&two).join(&69u64), - }, - signature: [1u8; 64], - }; - let tx2 = UncheckedTransaction { - transaction: Transaction { - signed: two.clone(), - nonce: 42, - function: Function::StakingStake, - input_data: Vec::new(), - }, - signature: [2u8; 64], - }; - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let b = Block { - header: h, - transactions: vec![tx1, tx2], - }; - let data = [ - // header - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, - // transactions - 2, 1, 0, 0, - // tx1 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 69, 0, 0, 0, 0, 0, 0, 0, - 2, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0, - // tx2 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 42, 0, 0, 0, 0, 0, 0, 0, - 0, - 0, 0, 0, 0 - ]; - let deserialised = Block::from_slice(&data).unwrap(); - assert_eq!(deserialised, b); - } -} diff --git a/substrate/wasm-runtime/polkadot/src/support/statichex.rs b/substrate/wasm-runtime/polkadot/src/support/statichex.rs index 152ceadc0b..b750a8ca10 100644 --- a/substrate/wasm-runtime/polkadot/src/support/statichex.rs +++ b/substrate/wasm-runtime/polkadot/src/support/statichex.rs @@ -36,7 +36,16 @@ macro_rules! impl_sizes { )* } } -impl_sizes!(1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128); +impl StaticHexConversion for Vec { + fn from_static_hex(hex: &'static str) -> Self { + FromHex::from_hex(hex).unwrap() + } +} + +impl_sizes!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 451, 42, 43, 44, 45, 46, 47, 48, + 56, 64, 80, 96, 112, 128); /// Trait to allow converting from itself (only implemented for a static str) into some useful /// type (which must implement `StaticHexConversion`). diff --git a/substrate/wasm-runtime/polkadot/src/support/storable.rs b/substrate/wasm-runtime/polkadot/src/support/storable.rs deleted file mode 100644 index 9a871a638b..0000000000 --- a/substrate/wasm-runtime/polkadot/src/support/storable.rs +++ /dev/null @@ -1,88 +0,0 @@ -// 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 . - -//! Stuff to do with the runtime's storage. - -use slicable::Slicable; -use endiansensitive::EndianSensitive; -use keyedvec::KeyedVec; -use runtime_support::prelude::*; -use runtime_support::{self, twox_128}; - -/// Trait for a value which may be stored in the storage DB. -pub trait Storable { - /// Lookup the value in storage and deserialise, giving a default value if not found. - fn lookup_default(key: &[u8]) -> Self where Self: Sized + Default { Self::lookup(key).unwrap_or_else(Default::default) } - /// Lookup `Some` value in storage and deserialise; `None` if it's not there. - fn lookup(_key: &[u8]) -> Option where Self: Sized { unimplemented!() } - /// Place the value in storage under `key`. - fn store(&self, key: &[u8]); -} - -// TODO: consider using blake256 to avoid possible eclipse attack. - -/// Remove `key` from storage. -pub fn kill(key: &[u8]) { runtime_support::set_storage(&twox_128(key)[..], b""); } - -impl Storable for T { - fn lookup(key: &[u8]) -> Option { - Slicable::set_as_slice(|out| runtime_support::read_storage(&twox_128(key)[..], out) == out.len()) - } - fn store(&self, key: &[u8]) { - self.as_slice_then(|slice| runtime_support::set_storage(&twox_128(key)[..], slice)); - } -} - -impl Storable for [u8] { - fn store(&self, key: &[u8]) { - runtime_support::set_storage(&twox_128(key)[..], self) - } -} - -/// A trait to conveniently store a vector of storable data. -// TODO: add iterator support -pub trait StorageVec { - type Item: Default + Sized + Storable; - const PREFIX: &'static [u8]; - - /// Get the current set of items. - fn items() -> Vec { - (0..Self::count()).into_iter().map(Self::item).collect() - } - - /// Set the current set of items. - fn set_items(items: &[Self::Item]) { - Self::set_count(items.len() as u32); - items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i)); - } - - fn set_item(index: u32, item: &Self::Item) { - item.store(&index.to_keyed_vec(Self::PREFIX)); - } - - fn item(index: u32) -> Self::Item { - Storable::lookup_default(&index.to_keyed_vec(Self::PREFIX)) - } - - fn set_count(count: u32) { - (count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default())); - count.store(&b"len".to_keyed_vec(Self::PREFIX)); - } - - fn count() -> u32 { - Storable::lookup_default(&b"len".to_keyed_vec(Self::PREFIX)) - } -} diff --git a/substrate/wasm-runtime/polkadot/src/support/storage.rs b/substrate/wasm-runtime/polkadot/src/support/storage.rs new file mode 100644 index 0000000000..3ff8b1c75d --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/support/storage.rs @@ -0,0 +1,229 @@ +// 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 . + +//! Stuff to do with the runtime's storage. + +use runtime_std::prelude::*; +use runtime_std::{self, twox_128}; +use codec::{Slicable, KeyedVec}; + +// TODO: consider using blake256 to avoid possible preimage attack. + +/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. +pub fn get(key: &[u8]) -> Option { + Slicable::set_as_slice(&|out, offset| + runtime_std::read_storage(&twox_128(key)[..], out, offset) >= out.len() + ) +} + +/// Return the value of the item in storage under `key`, or the type's default if there is no +/// explicit entry. +pub fn get_or_default(key: &[u8]) -> T { + get(key).unwrap_or_else(Default::default) +} + +/// Return the value of the item in storage under `key`, or `default_value` if there is no +/// explicit entry. +pub fn get_or(key: &[u8], default_value: T) -> T { + get(key).unwrap_or(default_value) +} + +/// Return the value of the item in storage under `key`, or `default_value()` if there is no +/// explicit entry. +pub fn get_or_else T>(key: &[u8], default_value: F) -> T { + get(key).unwrap_or_else(default_value) +} + +/// Please `value` in storage under `key`. +pub fn put(key: &[u8], value: &T) { + value.as_slice_then(|slice| runtime_std::set_storage(&twox_128(key)[..], slice)); +} + +/// Please `value` in storage under `key`. +pub fn place(key: &[u8], value: T) { + value.as_slice_then(|slice| runtime_std::set_storage(&twox_128(key)[..], slice)); +} + +/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. +pub fn take(key: &[u8]) -> Option { + let r = get(key); + if r.is_some() { + kill(key); + } + r +} + +/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage, +/// the default for its type. +pub fn take_or_default(key: &[u8]) -> T { + take(key).unwrap_or_else(Default::default) +} + +/// Return the value of the item in storage under `key`, or `default_value` if there is no +/// explicit entry. Ensure there is no explicit entry on return. +pub fn take_or(key: &[u8], default_value: T) -> T { + take(key).unwrap_or(default_value) +} + +/// Return the value of the item in storage under `key`, or `default_value()` if there is no +/// explicit entry. Ensure there is no explicit entry on return. +pub fn take_or_else T>(key: &[u8], default_value: F) -> T { + take(key).unwrap_or_else(default_value) +} + +/// Check to see if `key` has an explicit entry in storage. +pub fn exists(key: &[u8]) -> bool { + let mut x = [0u8; 1]; + runtime_std::read_storage(&twox_128(key)[..], &mut x[..], 0) >= 1 +} + +/// Ensure `key` has no explicit entry in storage. +pub fn kill(key: &[u8]) { + runtime_std::set_storage(&twox_128(key)[..], b""); +} + +/// Get a Vec of bytes from storage. +pub fn get_raw(key: &[u8]) -> Vec { + runtime_std::storage(&twox_128(key)[..]) +} + +/// Put a raw byte slice into storage. +pub fn put_raw(key: &[u8], value: &[u8]) { + runtime_std::set_storage(&twox_128(key)[..], value) +} + +/// A trait to conveniently store a vector of storable data. +// TODO: add iterator support +pub trait StorageVec { + type Item: Default + Sized + Slicable; + const PREFIX: &'static [u8]; + + /// Get the current set of items. + fn items() -> Vec { + (0..Self::count()).into_iter().map(Self::item).collect() + } + + /// Set the current set of items. + fn set_items(items: &[Self::Item]) { + Self::set_count(items.len() as u32); + items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i)); + } + + fn set_item(index: u32, item: &Self::Item) { + if index < Self::count() { + put(&index.to_keyed_vec(Self::PREFIX), item); + } + } + + fn item(index: u32) -> Self::Item { + get_or_default(&index.to_keyed_vec(Self::PREFIX)) + } + + fn set_count(count: u32) { + (count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default())); + put(&b"len".to_keyed_vec(Self::PREFIX), &count); + } + + fn count() -> u32 { + get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashMap; + use runtime_std::with_externalities; + use support::{TestExternalities, HexDisplay}; + use runtime_std::{storage, twox_128}; + + #[test] + fn integers_can_be_stored() { + let mut t = TestExternalities { storage: HashMap::new(), }; + with_externalities(&mut t, || { + let x = 69u32; + put(b":test", &x); + let y: u32 = get(b":test").unwrap(); + assert_eq!(x, y); + }); + with_externalities(&mut t, || { + let x = 69426942i64; + put(b":test", &x); + let y: i64 = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } + + #[test] + fn bools_can_be_stored() { + let mut t = TestExternalities { storage: HashMap::new(), }; + with_externalities(&mut t, || { + let x = true; + put(b":test", &x); + let y: bool = get(b":test").unwrap(); + assert_eq!(x, y); + }); + + with_externalities(&mut t, || { + let x = false; + put(b":test", &x); + let y: bool = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } + + #[test] + fn vecs_can_be_retrieved() { + let mut t = TestExternalities { storage: HashMap::new(), }; + with_externalities(&mut t, || { + runtime_std::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world"); + let x = b"Hello world".to_vec(); + println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")))); + let y = get::>(b":test").unwrap(); + assert_eq!(x, y); + + }); + } + + #[test] + fn vecs_can_be_stored() { + let mut t = TestExternalities { storage: HashMap::new(), }; + let x = b"Hello world".to_vec(); + + with_externalities(&mut t, || { + put(b":test", &x); + }); + + println!("Ext is {:?}", t); + with_externalities(&mut t, || { + println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")))); + let y: Vec = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } + + #[test] + fn proposals_can_be_stored() { + use primitives::{Proposal, InternalFunction}; + let mut t = TestExternalities { storage: HashMap::new(), }; + with_externalities(&mut t, || { + let x = Proposal { function: InternalFunction::StakingSetSessionsPerEra, input_data: b"Hello world".to_vec() }; + put(b":test", &x); + let y: Proposal = get(b":test").unwrap(); + assert_eq!(x, y); + }); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/support/testing.rs b/substrate/wasm-runtime/polkadot/src/support/testing.rs index e2e7b40562..e0e1a336c8 100644 --- a/substrate/wasm-runtime/polkadot/src/support/testing.rs +++ b/substrate/wasm-runtime/polkadot/src/support/testing.rs @@ -16,10 +16,10 @@ //! Testing helpers. -use runtime_support::{NoError, Externalities}; use std::collections::HashMap; +use runtime_std::{Externalities, ExternalitiesError}; use primitives::AccountID; -use statichex::StaticHexInto; +use super::statichex::StaticHexInto; #[derive(Debug, Default)] /// Simple externaties implementation. @@ -29,9 +29,7 @@ pub struct TestExternalities { } impl Externalities for TestExternalities { - type Error = NoError; - - fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> { + fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } diff --git a/substrate/wasm-runtime/pwasm-libc/src/lib.rs b/substrate/wasm-runtime/pwasm-libc/src/lib.rs index eed597e73b..f4e5eb7a57 100644 --- a/substrate/wasm-runtime/pwasm-libc/src/lib.rs +++ b/substrate/wasm-runtime/pwasm-libc/src/lib.rs @@ -8,6 +8,7 @@ extern "C" { fn ext_memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; fn ext_memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; fn ext_memset(dest: *mut u8, c: i32, n: usize) -> *mut u8; + fn ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; fn ext_malloc(size: usize) -> *mut u8; fn ext_free(ptr: *mut u8); } @@ -21,6 +22,12 @@ pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut ext_memcpy(dest, src, n) } +/// memcmp extern +#[no_mangle] +pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + ext_memcmp(s1, s2, n) +} + /// memmove extern #[no_mangle] pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { diff --git a/substrate/wasm-runtime/support/Cargo.toml b/substrate/wasm-runtime/std/Cargo.toml similarity index 90% rename from substrate/wasm-runtime/support/Cargo.toml rename to substrate/wasm-runtime/std/Cargo.toml index 828fdab534..802d1fabde 100644 --- a/substrate/wasm-runtime/support/Cargo.toml +++ b/substrate/wasm-runtime/std/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "runtime-support" +name = "runtime-std" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/substrate/wasm-runtime/support/src/lib.rs b/substrate/wasm-runtime/std/src/lib.rs similarity index 55% rename from substrate/wasm-runtime/support/src/lib.rs rename to substrate/wasm-runtime/std/src/lib.rs index 90c59a6e74..9b7d385e69 100644 --- a/substrate/wasm-runtime/support/src/lib.rs +++ b/substrate/wasm-runtime/std/src/lib.rs @@ -1,9 +1,9 @@ #![no_std] #![feature(lang_items)] +#![feature(core_intrinsics)] #![feature(alloc)] #![cfg_attr(feature = "strict", deny(warnings))] -#![feature(alloc)] extern crate alloc; pub use alloc::vec; @@ -26,16 +26,22 @@ extern crate pwasm_alloc; #[lang = "panic_fmt"] #[no_mangle] -pub fn panic_fmt() -> ! { - loop {} +pub extern fn panic_fmt(_fmt: ::core::fmt::Arguments, _file: &'static str, _line: u32, _col: u32) { + unsafe { + ext_print_utf8(_file.as_ptr() as *const u8, _file.len() as u32); + ext_print_num(_line as u64); + ext_print_num(_col as u64); + ::core::intrinsics::abort() + } } extern "C" { - fn ext_print(utf8_data: *const u8, utf8_len: u32); + fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32); + fn ext_print_hex(data: *const u8, len: u32); fn ext_print_num(value: u64); 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) -> u32; + 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_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); @@ -46,7 +52,7 @@ extern "C" { pub fn storage(key: &[u8]) -> Vec { let mut length: u32 = 0; unsafe { - let ptr = ext_get_allocated_storage(&key[0], key.len() as u32, &mut length); + let ptr = ext_get_allocated_storage(key.as_ptr(), key.len() as u32, &mut length); Vec::from_raw_parts(ptr, length as usize, length as usize) } } @@ -54,15 +60,15 @@ pub fn storage(key: &[u8]) -> Vec { pub fn set_storage(key: &[u8], value: &[u8]) { unsafe { ext_set_storage( - &key[0] as *const u8, key.len() as u32, - &value[0] as *const u8, value.len() as u32 + key.as_ptr(), key.len() as u32, + value.as_ptr(), value.len() as u32 ); } } -pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize { unsafe { - ext_get_storage_into(&key[0], key.len() as u32, &mut value_out[0], value_out.len() as u32) as usize + ext_get_storage_into(key.as_ptr(), key.len() as u32, value_out.as_mut_ptr(), value_out.len() as u32, value_offset as u32) as usize } } @@ -75,38 +81,38 @@ 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 { - let mut result: [u8; 32] = Default::default(); - // guaranteed to write into result. - ext_blake2_256(&data[0], data.len() as u32, &mut result[0]); - result + ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); } + result } /// 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 { - let mut result: [u8; 32] = Default::default(); - // guaranteed to write into result. - ext_twox_256(&data[0], data.len() as u32, &mut result[0]); - result + ext_twox_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); } + result } -/// Conduct two XX hashes to give a 256-bit result. +/// 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 { - let mut result: [u8; 16] = Default::default(); - // guaranteed to write into result. - ext_twox_128(&data[0], data.len() as u32, &mut result[0]); - result + ext_twox_128(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); } + result } /// Verify a ed25519 signature. pub fn ed25519_verify(sig: &[u8], msg: &[u8], pubkey: &[u8]) -> bool { - sig.len() != 64 || pubkey.len() != 32 || unsafe { - ext_ed25519_verify(&msg[0], msg.len() as u32, &sig[0], &pubkey[0]) + sig.len() == 64 && pubkey.len() == 32 && unsafe { + ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ptr()) } == 0 } @@ -117,7 +123,15 @@ pub trait Printable { impl<'a> Printable for &'a [u8] { fn print(self) { unsafe { - ext_print(self.as_ptr(), self.len() as u32); + ext_print_hex(self.as_ptr(), self.len() as u32); + } + } +} + +impl<'a> Printable for &'a str { + fn print(self) { + unsafe { + ext_print_utf8(self.as_ptr() as *const u8, self.len() as u32); } } } @@ -139,12 +153,16 @@ macro_rules! impl_stubs { $( #[no_mangle] pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { - let input = unsafe { - $crate::vec::Vec::from_raw_parts(input_data, input_len, input_len) + let input = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::slice::from_raw_parts(input_data, input_len) + } }; let output = super::$name(input); - &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) + output.as_ptr() as u64 + ((output.len() as u64) << 32) } )* } diff --git a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm index c1051d3414..db7dab9917 100644 Binary files a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm and b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm differ diff --git a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm index 231fa8af91..a0dc5a2754 100644 Binary files a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm and b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm differ diff --git a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index b1357890d8..63e7666aad 100644 Binary files a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm index 646b019bc0..c1b257f3a3 100644 Binary files a/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/wasm-runtime/test/Cargo.toml b/substrate/wasm-runtime/test/Cargo.toml index 57015d2683..c7191fb805 100644 --- a/substrate/wasm-runtime/test/Cargo.toml +++ b/substrate/wasm-runtime/test/Cargo.toml @@ -7,4 +7,4 @@ authors = ["Parity Technologies "] crate-type = ["cdylib"] [dependencies] -runtime-support = { path = "../support", version = "0.1" } +runtime-std = { path = "../std", version = "0.1" } diff --git a/substrate/wasm-runtime/test/src/lib.rs b/substrate/wasm-runtime/test/src/lib.rs index 1cf1934976..c63ca73fca 100644 --- a/substrate/wasm-runtime/test/src/lib.rs +++ b/substrate/wasm-runtime/test/src/lib.rs @@ -7,40 +7,56 @@ extern crate alloc; use alloc::vec::Vec; #[macro_use] -extern crate runtime_support; -use runtime_support::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify}; +extern crate runtime_std; +use runtime_std::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify}; -fn test_blake2_256(input: Vec) -> Vec { +fn test_blake2_256(input: &[u8]) -> Vec { blake2_256(&input).to_vec() } -fn test_twox_256(input: Vec) -> Vec { +fn test_twox_256(input: &[u8]) -> Vec { twox_256(&input).to_vec() } -fn test_twox_128(input: Vec) -> Vec { +fn test_twox_128(input: &[u8]) -> Vec { twox_128(&input).to_vec() } -fn test_ed25519_verify(input: Vec) -> Vec { +fn test_ed25519_verify(input: &[u8]) -> Vec { let sig = &input[0..64]; let pubkey = &input[64..96]; let msg = b"all ok!"; [ed25519_verify(sig, &msg[..], pubkey) as u8].to_vec() } -fn test_data_in(input: Vec) -> Vec { - print(b"set_storage" as &[u8]); +fn test_data_in(input: &[u8]) -> Vec { + print("set_storage"); set_storage(b"input", &input); - print(b"storage" as &[u8]); + print("storage"); let foo = storage(b"foo"); - print(b"set_storage" as &[u8]); + print("set_storage"); set_storage(b"baz", &foo); - print(b"finished!" as &[u8]); + print("finished!"); b"all ok!".to_vec() } -impl_stubs!(test_data_in, test_blake2_256, test_twox_256, test_twox_128, test_ed25519_verify); +fn test_empty_return(_input: &[u8]) -> Vec { + Vec::new() +} + +fn test_panic(_input: &[u8]) -> Vec { + panic!("test panic"); +} + +fn test_conditional_panic(input: &[u8]) -> Vec { + if input.len() > 0 { + panic!("test panic"); + } + input.to_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);