Merge pull request #47 from paritytech/native-executor

Native executor
This commit is contained in:
Gav Wood
2018-01-30 10:57:58 +01:00
committed by GitHub
57 changed files with 2410 additions and 1134 deletions
+5 -2
View File
@@ -553,7 +553,7 @@ dependencies = [
name = "native-runtime" name = "native-runtime"
version = "0.1.0" version = "0.1.0"
dependencies = [ 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)", "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)", "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)", "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)", "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)", "parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)",
"polkadot-primitives 0.1.0", "polkadot-primitives 0.1.0",
"polkadot-serializer 0.1.0", "polkadot-serializer 0.1.0",
"polkadot-state-machine 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)", "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 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)", "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -913,7 +916,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "runtime-support" name = "runtime-std"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"environmental 0.1.0", "environmental 0.1.0",
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
cd wasm-runtime
./build.sh
cd ..
+3
View File
@@ -13,6 +13,9 @@ serde_derive = "1.0"
parity-wasm = "0.15.0" parity-wasm = "0.15.0"
byteorder = "1.1" byteorder = "1.1"
rustc-hex = "1.0.0" 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] [dev-dependencies]
assert_matches = "1.1" assert_matches = "1.1"
+8 -3
View File
@@ -17,7 +17,6 @@
//! Rust executor possible errors. //! Rust executor possible errors.
use serializer; use serializer;
use state_machine;
error_chain! { error_chain! {
foreign_links { foreign_links {
@@ -38,9 +37,9 @@ error_chain! {
} }
/// Externalities have failed. /// Externalities have failed.
Externalities(e: Box<state_machine::Error>) { Externalities {
description("externalities failure"), description("externalities failure"),
display("Externalities error: {}", e), display("Externalities error"),
} }
/// Invalid index. /// Invalid index.
@@ -60,5 +59,11 @@ error_chain! {
description("runtime failure"), description("runtime failure"),
display("Runtime error"), display("Runtime error"),
} }
/// Runtime failed.
InvalidMemoryReference {
description("invalid memory reference"),
display("Invalid memory reference"),
}
} }
} }
+6 -2
View File
@@ -34,6 +34,9 @@ extern crate serde;
extern crate parity_wasm; extern crate parity_wasm;
extern crate byteorder; extern crate byteorder;
extern crate rustc_hex; extern crate rustc_hex;
extern crate native_runtime;
extern crate runtime_std;
extern crate libc;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
@@ -44,10 +47,11 @@ extern crate assert_matches;
#[macro_use] #[macro_use]
mod wasm_utils; mod wasm_utils;
mod wasm_executor; mod wasm_executor;
mod native_executor;
pub mod error; pub mod error;
/// Creates new RustExecutor for contracts. /// Creates new RustExecutor for contracts.
pub fn executor() -> wasm_executor::WasmExecutor { pub fn executor() -> native_executor::NativeExecutor {
wasm_executor::WasmExecutor::default() native_executor::NativeExecutor
} }
+110
View File
@@ -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<F: ::std::panic::UnwindSafe + FnOnce() -> Vec<u8>>(f: F) -> Result<Vec<u8>> {
catch_unwind(f).map_err(|_| ErrorKind::Runtime.into())
}
impl CodeExecutor for NativeExecutor {
type Error = Error;
fn call<E: Externalities>(
&self,
ext: &mut E,
code: &[u8],
method: &str,
data: &CallData,
) -> Result<Vec<u8>> {
// 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<u8> { "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);
});
}
}
+121 -42
View File
@@ -17,9 +17,10 @@
//! Rust implementation of Polkadot contracts. //! Rust implementation of Polkadot contracts.
use std::sync::Arc; use std::sync::Arc;
use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; 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 parity_wasm::RuntimeValue::{I32, I64};
use primitives::contract::CallData; use primitives::contract::CallData;
use state_machine::{Externalities, CodeExecutor}; use state_machine::{Externalities, CodeExecutor};
@@ -27,6 +28,7 @@ use error::{Error, ErrorKind, Result};
use wasm_utils::{MemoryInstance, UserDefinedElements, use wasm_utils::{MemoryInstance, UserDefinedElements,
AddModuleWithoutFullDependentInstance}; AddModuleWithoutFullDependentInstance};
use primitives::{ed25519, blake2_256, twox_128, twox_256}; use primitives::{ed25519, blake2_256, twox_128, twox_256};
use primitives::hexdisplay::HexDisplay;
struct Heap { struct Heap {
end: u32, end: u32,
@@ -77,16 +79,33 @@ impl WritePrimitive<u32> for MemoryInstance {
} }
impl_function_executor!(this: FunctionExecutor<'e, E>, 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(utf8) = this.memory.get(utf8_data, utf8_len as usize) {
if let Ok(message) = String::from_utf8(utf8) { if let Ok(message) = String::from_utf8(utf8) {
println!("Runtime: {}", message); 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) => { ext_print_num(number: u64) => {
println!("Runtime: {}", number); 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 => { 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); let _ = this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize);
println!("memcpy {} from {}, {} bytes", dest, src, count); 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); this.memory.write_primitive(written_out, written);
offset as u32 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(key) = this.memory.get(key_data, key_len as usize) {
if let Ok(value) = this.ext.storage(&key) { 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 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 written as u32
} else { 0 } } else { 0 }
} else { 0 } } else { 0 }
@@ -141,47 +161,63 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
this.ext.chain_id() this.ext.chain_id()
}, },
ext_twox_128(data: *const u8, len: u32, out: *mut u8) => { ext_twox_128(data: *const u8, len: u32, out: *mut u8) => {
let result = let maybe_value = if len == 0 {
if let Ok(value) = this.memory.get(data, len as usize) { Ok(vec![])
twox_128(&value) } else {
} else { this.memory.get(data, len as usize)
[0; 16] };
}; let result = if let Ok(value) = maybe_value {
twox_128(&value)
} else {
[0; 16]
};
let _ = this.memory.set(out, &result); let _ = this.memory.set(out, &result);
}, },
ext_twox_256(data: *const u8, len: u32, out: *mut u8) => { ext_twox_256(data: *const u8, len: u32, out: *mut u8) => {
let result = let maybe_value = if len == 0 {
if let Ok(value) = this.memory.get(data, len as usize) { Ok(vec![])
twox_256(&value) } else {
} else { this.memory.get(data, len as usize)
[0; 32] };
}; let result = if let Ok(value) = maybe_value {
twox_256(&value)
} else {
[0; 32]
};
let _ = this.memory.set(out, &result); let _ = this.memory.set(out, &result);
}, },
ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => { ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => {
let result = let maybe_value = if len == 0 {
if let Ok(value) = this.memory.get(data, len as usize) { Ok(vec![])
blake2_256(&value) } else {
} else { this.memory.get(data, len as usize)
[0; 32] };
}; let result = if let Ok(value) = maybe_value {
blake2_256(&value)
} else {
[0; 32]
};
let _ = this.memory.set(out, &result); 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 => { ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => {
(||{ (||{
let mut sig = [0u8; 64]; let mut sig = [0u8; 64];
if let Err(_) = this.memory.get_into(sig_data, &mut sig[..]) { if let Err(_) = this.memory.get_into(sig_data, &mut sig[..]) {
return 0; return 2;
}; };
let mut pubkey = [0u8; 32]; let mut pubkey = [0u8; 32];
if let Err(_) = this.memory.get_into(pubkey_data, &mut pubkey[..]) { 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 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 { } else {
0 4
} }
})() })()
} }
@@ -224,7 +260,9 @@ impl CodeExecutor for WasmExecutor {
.add_argument(I32(offset as i32)) .add_argument(I32(offset as i32))
.add_argument(I32(size as i32))) .add_argument(I32(size as i32)))
.and_then(|p| module.execute_export(method, p)) .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 { if let Some(I64(r)) = returned {
memory.get(r as u32, (r >> 32) as u32 as usize) memory.get(r as u32, (r >> 32) as u32 as usize)
@@ -240,23 +278,31 @@ mod tests {
use super::*; use super::*;
use rustc_hex::FromHex; 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)] #[test]
struct TestExternalities { fn returning_should_work() {
storage: HashMap<Vec<u8>, Vec<u8>>, 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]> { #[test]
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) 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<u8>, value: Vec<u8>) { let output = WasmExecutor.call(&mut ext, &test_code[..], "test_panic", &CallData(vec![]));
self.storage.insert(key, value); 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] #[test]
@@ -283,11 +329,11 @@ mod tests {
let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); let test_code = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!( assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_blake2_256", &CallData(b"".to_vec())).unwrap(), 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!( assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_blake2_256", &CallData(b"Hello world!".to_vec())).unwrap(), 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()); calldata.extend_from_slice(sig.as_ref());
assert_eq!( assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_ed25519_verify", &CallData(calldata)).unwrap(), WasmExecutor.call(&mut ext, &test_code[..], "test_ed25519_verify", &CallData(calldata)).unwrap(),
vec![1] vec![0]
); );
} }
fn tx() -> Vec<u8> { "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);
});
}
} }
+1 -1
View File
@@ -9,5 +9,5 @@ with-std = []
without-std = [] without-std = []
[dependencies] [dependencies]
runtime-support = { path = "./support", version = "0.1" } runtime-std = { path = "./std", version = "0.1" }
rustc-hex = "1.0" rustc-hex = "1.0"
@@ -1,5 +1,5 @@
[package] [package]
name = "runtime-support" name = "runtime-std"
version = "0.1.0" version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
@@ -39,7 +39,8 @@ pub mod prelude {
pub use std::boxed::Box; 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. // 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, "") } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") }
} }
environmental!(ext : Externalities<Error=NoError> + 'static); environmental!(ext : trait Externalities);
/// Get `key` from storage and return a `Vec`, empty if there's a problem. /// Get `key` from storage and return a `Vec`, empty if there's a problem.
pub fn storage(key: &[u8]) -> Vec<u8> { pub fn storage(key: &[u8]) -> Vec<u8> {
ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec())) ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec()))
.unwrap_or(None) .expect("read_storage cannot be called outside of an Externalities-provided environment.")
.unwrap_or_else(|| vec![]) .unwrap_or_else(Vec::new)
} }
/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return /// 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. /// 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| { ext::with(|ext| {
if let Ok(value) = ext.storage(key) { if let Ok(value) = ext.storage(key) {
let value = &value[value_offset..];
let written = ::std::cmp::min(value.len(), value_out.len()); let written = ::std::cmp::min(value.len(), value_out.len());
value_out[0..written].copy_from_slice(&value[0..written]); value_out[0..written].copy_from_slice(&value[0..written]);
value.len() value.len()
} else { } 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<usize>)
0 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]) { pub fn set_storage(key: &[u8], value: &[u8]) {
ext::with(|ext| ext::with(|ext|
ext.set_storage(key.to_vec(), value.to_vec()) 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 /// Execute the given closure with global function available whose functionality routes into the
/// externalities `ext`. Forwards the value that the closure returns. /// externalities `ext`. Forwards the value that the closure returns.
pub fn with_externalities<R, F: FnOnce() -> R>(ext: &mut (Externalities<Error=NoError> + 'static), f: F) -> R { pub fn with_externalities<R, F: FnOnce() -> R>(ext: &mut Externalities, f: F) -> R {
ext::using(ext, f) 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<T: Printable + Sized>(value: T) {
value.print();
}
#[macro_export] #[macro_export]
macro_rules! impl_stubs { macro_rules! impl_stubs {
($( $name:ident ),*) => {} ($( $name:ident ),*) => {}
@@ -115,9 +147,7 @@ mod tests {
storage: HashMap<Vec<u8>, Vec<u8>>, storage: HashMap<Vec<u8>, Vec<u8>>,
} }
impl Externalities for TestExternalities { impl Externalities for TestExternalities {
type Error = NoError; fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
} }
@@ -154,4 +184,20 @@ mod tests {
false 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");
});
}
} }
+4
View File
@@ -39,6 +39,10 @@ pub trait AsBytesRef {
fn as_bytes_ref(&self) -> &[u8]; fn as_bytes_ref(&self) -> &[u8];
} }
impl<'a> AsBytesRef for &'a [u8] {
fn as_bytes_ref(&self) -> &[u8] { self }
}
impl AsBytesRef for [u8] { impl AsBytesRef for [u8] {
fn as_bytes_ref(&self) -> &[u8] { &self } fn as_bytes_ref(&self) -> &[u8] { &self }
} }
+3 -5
View File
@@ -19,7 +19,7 @@
use std::{error, fmt}; use std::{error, fmt};
use backend::Backend; use backend::Backend;
use {Externalities, OverlayedChanges}; use {Externalities, ExternalitiesError, OverlayedChanges};
/// Errors that can occur when interacting with the externalities. /// Errors that can occur when interacting with the externalities.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -61,12 +61,10 @@ pub struct Ext<'a, B: 'a> {
impl<'a, B: 'a> Externalities for Ext<'a, B> impl<'a, B: 'a> Externalities for Ext<'a, B>
where B: Backend where B: Backend
{ {
type Error = B::Error; fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> {
match self.overlay.storage(key) { match self.overlay.storage(key) {
Some(x) => Ok(x), Some(x) => Ok(x),
None => self.backend.storage(key) None => self.backend.storage(key).map_err(|_| ExternalitiesError),
} }
} }
+16 -9
View File
@@ -113,6 +113,18 @@ impl OverlayedChanges {
pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} pub trait Error: 'static + fmt::Debug + fmt::Display + Send {}
impl<E> Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} impl<E> 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<u8>) -> Vec<u8> { fn to_keyed_vec(value: u32, mut prepend: Vec<u8>) -> Vec<u8> {
prepend.extend((0..::std::mem::size_of::<u32>()).into_iter().map(|i| (value >> (i * 8)) as u8)); prepend.extend((0..::std::mem::size_of::<u32>()).into_iter().map(|i| (value >> (i * 8)) as u8));
prepend prepend
@@ -120,11 +132,8 @@ fn to_keyed_vec(value: u32, mut prepend: Vec<u8>) -> Vec<u8> {
/// Externalities: pinned to specific active address. /// Externalities: pinned to specific active address.
pub trait Externalities { pub trait Externalities {
/// Externalities error type.
type Error: Error;
/// Read storage of current contract being called. /// 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). /// Set storage of current contract being called (effective immediately).
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>); fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>);
@@ -133,7 +142,7 @@ pub trait Externalities {
fn chain_id(&self) -> u64; fn chain_id(&self) -> u64;
/// Get the current set of authorities from storage. /// Get the current set of authorities from storage.
fn authorities(&self) -> Result<Vec<&[u8]>, Self::Error> { fn authorities(&self) -> Result<Vec<&[u8]>, ExternalitiesError> {
(0..self.storage(b"con:aut:len")?.into_iter() (0..self.storage(b"con:aut:len")?.into_iter()
.rev() .rev()
.fold(0, |acc, &i| (acc << 8) + (i as u32))) .fold(0, |acc, &i| (acc << 8) + (i as u32)))
@@ -202,7 +211,7 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use super::{OverlayedChanges, Externalities}; use super::{OverlayedChanges, Externalities, ExternalitiesError};
#[test] #[test]
fn overlayed_storage_works() { fn overlayed_storage_works() {
@@ -234,9 +243,7 @@ mod tests {
storage: HashMap<Vec<u8>, Vec<u8>>, storage: HashMap<Vec<u8>, Vec<u8>>,
} }
impl Externalities for TestExternalities { impl Externalities for TestExternalities {
type Error = u8; fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
} }
+3 -3
View File
@@ -13,11 +13,11 @@ version = "0.1.0"
name = "runtime-polkadot" name = "runtime-polkadot"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"runtime-support 0.1.0", "runtime-std 0.1.0",
] ]
[[package]] [[package]]
name = "runtime-support" name = "runtime-std"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"pwasm-alloc 0.1.0", "pwasm-alloc 0.1.0",
@@ -28,6 +28,6 @@ dependencies = [
name = "runtime-test" name = "runtime-test"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"runtime-support 0.1.0", "runtime-std 0.1.0",
] ]
+2 -1
View File
@@ -1,7 +1,8 @@
#!/bin/sh #!/bin/sh
set -e
cargo +nightly build --target=wasm32-unknown-unknown --release 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 for i in $dirs
do do
if [[ -e $i/Cargo.toml ]] if [[ -e $i/Cargo.toml ]]
+1 -1
View File
@@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
runtime-support = { path = "../support", version = "0.1" } runtime-std = { path = "../std", version = "0.1" }
[features] [features]
default = ["without-std"] default = ["without-std"]
@@ -17,7 +17,9 @@
//! Endian manager. //! Endian manager.
/// Trait to allow conversion to a know endian representation when sensitive. /// 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 { pub trait EndianSensitive: Copy + 'static {
fn to_le(self) -> Self { self } fn to_le(self) -> Self { self }
fn to_be(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_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], 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], [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<T: 'static>() { } fn _takes_static<T: 'static>() { }
fn _takes_endian_sensitive<T: EndianSensitive>() { _takes_static::<T>() } fn _takes_endian_sensitive<T: EndianSensitive>() { _takes_static::<T>() }
} }
#[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());
}
} }
@@ -16,8 +16,8 @@
//! Vec<u8> serialiser. //! Vec<u8> serialiser.
use runtime_support::prelude::*; use runtime_std::prelude::*;
use slicable::Slicable; use super::slicable::Slicable;
/// Trait to allow itself to be serialised into a `Vec<u8>` /// Trait to allow itself to be serialised into a `Vec<u8>`
pub trait Joiner { pub trait Joiner {
@@ -16,8 +16,8 @@
//! Serialiser and prepender. //! Serialiser and prepender.
use runtime_support::prelude::*; use runtime_std::prelude::*;
use slicable::Slicable; use super::slicable::Slicable;
/// Trait to allow itselg to be serialised and prepended by a given slice. /// Trait to allow itselg to be serialised and prepended by a given slice.
pub trait KeyedVec { pub trait KeyedVec {
@@ -16,8 +16,14 @@
//! Codec utils. //! Codec utils.
pub mod endiansensitive; mod endiansensitive;
pub mod streamreader; mod slicable;
pub mod joiner; mod streamreader;
pub mod slicable; mod joiner;
pub mod keyedvec; 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;
@@ -16,16 +16,18 @@
//! Serialisation. //! Serialisation.
use runtime_support::prelude::*; use runtime_std::prelude::*;
use runtime_support::{mem, slice}; use runtime_std::{mem, slice};
use joiner::Joiner; use super::joiner::Joiner;
use endiansensitive::EndianSensitive; use super::endiansensitive::EndianSensitive;
/// Trait that allows zero-copy read/write of value-references to/from slices in LE format. /// Trait that allows zero-copy read/write of value-references to/from slices in LE format.
pub trait Slicable: Sized { pub trait Slicable: Sized {
fn from_slice(value: &[u8]) -> Option<Self> { fn from_slice(value: &[u8]) -> Option<Self> {
Self::set_as_slice(|out| if value.len() == out.len() { Self::set_as_slice(&|out, offset| if value.len() >= out.len() + offset {
out.copy_from_slice(&value); let value = &value[offset..];
let len = out.len();
out.copy_from_slice(&value[0..len]);
true true
} else { } else {
false false
@@ -34,7 +36,7 @@ pub trait Slicable: Sized {
fn to_vec(&self) -> Vec<u8> { fn to_vec(&self) -> Vec<u8> {
self.as_slice_then(|s| s.to_vec()) self.as_slice_then(|s| s.to_vec())
} }
fn set_as_slice<F: FnOnce(&mut [u8]) -> bool>(set_slice: F) -> Option<Self>; fn set_as_slice<F: Fn(&mut [u8], usize) -> bool>(set_slice: &F) -> Option<Self>;
fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&self.to_vec()) f(&self.to_vec())
} }
@@ -45,14 +47,15 @@ pub trait Slicable: Sized {
pub trait NonTrivialSlicable: Slicable {} pub trait NonTrivialSlicable: Slicable {}
impl<T: EndianSensitive> Slicable for T { impl<T: EndianSensitive> Slicable for T {
fn set_as_slice<F: FnOnce(&mut [u8]) -> bool>(fill_slice: F) -> Option<Self> { fn set_as_slice<F: Fn(&mut [u8], usize) -> bool>(fill_slice: &F) -> Option<Self> {
let size = mem::size_of::<T>(); let size = mem::size_of::<T>();
assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type.");
let mut result: T = unsafe { mem::zeroed() }; let mut result: T = unsafe { mem::zeroed() };
let result_slice = unsafe { let result_slice = unsafe {
let ptr = &mut result as *mut _ as *mut u8; let ptr = &mut result as *mut _ as *mut u8;
slice::from_raw_parts_mut(ptr, size) slice::from_raw_parts_mut(ptr, size)
}; };
if fill_slice(result_slice) { if fill_slice(result_slice, 0) {
Some(result.from_le()) Some(result.from_le())
} else { } else {
None None
@@ -60,6 +63,7 @@ impl<T: EndianSensitive> Slicable for T {
} }
fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
let size = mem::size_of::<Self>(); let size = mem::size_of::<Self>();
assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type.");
self.as_le_then(|le| { self.as_le_then(|le| {
let value_slice = unsafe { let value_slice = unsafe {
let ptr = le as *const _ as *const u8; let ptr = le as *const _ as *const u8;
@@ -77,8 +81,16 @@ impl Slicable for Vec<u8> {
fn from_slice(value: &[u8]) -> Option<Self> { fn from_slice(value: &[u8]) -> Option<Self> {
Some(value[4..].to_vec()) Some(value[4..].to_vec())
} }
fn set_as_slice<F: FnOnce(&mut [u8]) -> bool>(_fill_slice: F) -> Option<Self> { fn set_as_slice<F: Fn(&mut [u8], usize) -> bool>(fill_slice: &F) -> Option<Self> {
unimplemented!(); 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<u8> { fn to_vec(&self) -> Vec<u8> {
let mut r: Vec<u8> = Vec::new().join(&(self.len() as u32)); let mut r: Vec<u8> = Vec::new().join(&(self.len() as u32));
@@ -89,3 +101,48 @@ impl Slicable for Vec<u8> {
u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize)
} }
} }
impl<T: Slicable> NonTrivialSlicable for Vec<T> where Vec<T>: Slicable {}
impl<T: NonTrivialSlicable> Slicable for Vec<T> {
fn from_slice(value: &[u8]) -> Option<Self> {
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<F: Fn(&mut [u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
let vecs = self.iter().map(Slicable::to_vec).collect::<Vec<_>>();
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<usize> {
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")
);
}
}
@@ -16,7 +16,7 @@
//! Deserialiser. //! Deserialiser.
use slicable::Slicable; use super::slicable::Slicable;
/// Simple deserialiser. /// Simple deserialiser.
pub struct StreamReader<'a> { pub struct StreamReader<'a> {
+13 -15
View File
@@ -20,34 +20,32 @@
#![cfg_attr(feature = "strict", deny(warnings))] #![cfg_attr(feature = "strict", deny(warnings))]
#[macro_use] #[macro_use]
extern crate runtime_support; extern crate runtime_std;
#[cfg(test)] #[cfg(feature = "with-std")]
extern crate rustc_hex; extern crate rustc_hex;
mod codec; pub mod codec;
#[macro_use] #[macro_use]
mod support; pub mod support;
mod runtime; pub mod primitives;
pub use codec::{endiansensitive, streamreader, joiner, slicable, keyedvec}; pub mod runtime;
pub use support::{primitives, function, environment, storable};
#[cfg(test)]
pub use support::{testing, statichex};
use runtime_support::prelude::*; use runtime_std::prelude::*;
use slicable::Slicable; use codec::Slicable;
use primitives::{Block, UncheckedTransaction}; use primitives::{Block, UncheckedTransaction};
/// Execute a block, with `input` being the canonical serialisation of the block. Returns the /// Execute a block, with `input` being the canonical serialisation of the block. Returns the
/// empty vector. /// empty vector.
pub fn execute_block(input: Vec<u8>) -> Vec<u8> { pub fn execute_block(input: &[u8]) -> Vec<u8> {
runtime::system::execute_block(Block::from_slice(&input).unwrap()); runtime::system::internal::execute_block(Block::from_slice(input).unwrap());
Vec::new() Vec::new()
} }
/// Execute a given, serialised, transaction. Returns the empty vector. /// Execute a given, serialised, transaction. Returns the empty vector.
pub fn execute_transaction(input: Vec<u8>) -> Vec<u8> { pub fn execute_transaction(input: &[u8]) -> Vec<u8> {
runtime::system::execute_transaction(&UncheckedTransaction::from_slice(&input).unwrap()); let utx = UncheckedTransaction::from_slice(input).unwrap();
runtime::system::internal::execute_transaction(&utx);
Vec::new() Vec::new()
} }
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<UncheckedTransaction>,
}
impl Slicable for Block {
fn from_slice(value: &[u8]) -> Option<Self> {
let mut reader = StreamReader::new(value);
Some(Block {
header: reader.read()?,
transactions: reader.read()?,
})
}
fn set_as_slice<F: Fn(&mut [u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.header)
.join(&self.transactions)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = Header::size_of(data)?;
let second_part = <Vec<UncheckedTransaction>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl NonTrivialSlicable for Block {}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Vec<u8>>,
}
@@ -17,30 +17,34 @@
//! Function data: This describes a function that can be called from an external transaction. //! Function data: This describes a function that can be called from an external transaction.
use primitives::AccountID; use primitives::AccountID;
use streamreader::StreamReader; use codec::StreamReader;
use runtime::{staking, session, timestamp}; use runtime::{staking, session, timestamp, governance};
/// The functions that a transaction can call (and be dispatched to). /// Public functions that can be dispatched to.
#[cfg_attr(test, derive(PartialEq, Debug))]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[cfg_attr(feature = "with-std", derive(PartialEq, Debug))]
#[repr(u8)]
pub enum Function { pub enum Function {
StakingStake, StakingStake = 0,
StakingUnstake, StakingUnstake = 1,
StakingTransfer, StakingTransfer = 2,
SessionSetKey, SessionSetKey = 3,
TimestampSet, TimestampSet = 4,
GovernancePropose = 5,
GovernanceApprove = 6,
} }
impl Function { impl Function {
/// Derive `Some` value from a `u8`, or `None` if it's invalid. /// Derive `Some` value from a `u8`, or `None` if it's invalid.
pub fn from_u8(value: u8) -> Option<Function> { pub fn from_u8(value: u8) -> Option<Function> {
match value { use self::*;
x if x == Function::StakingStake as u8 => Some(Function::StakingStake), let functions = [Function::StakingStake, Function::StakingUnstake,
x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake), Function::StakingTransfer, Function::SessionSetKey, Function::TimestampSet,
x if x == Function::StakingTransfer as u8 => Some(Function::StakingTransfer), Function::GovernancePropose, Function::GovernanceApprove];
x if x == Function::SessionSetKey as u8 => Some(Function::SessionSetKey), if (value as usize) < functions.len() {
x if x == Function::TimestampSet as u8 => Some(Function::TimestampSet), Some(functions[value as usize])
_ => None, } else {
None
} }
} }
} }
@@ -51,23 +55,31 @@ impl Function {
let mut params = StreamReader::new(data); let mut params = StreamReader::new(data);
match *self { match *self {
Function::StakingStake => { Function::StakingStake => {
staking::stake(transactor); staking::public::stake(transactor);
} }
Function::StakingUnstake => { Function::StakingUnstake => {
staking::unstake(transactor); staking::public::unstake(transactor);
} }
Function::StakingTransfer => { Function::StakingTransfer => {
let dest = params.read().unwrap(); let dest = params.read().unwrap();
let value = params.read().unwrap(); let value = params.read().unwrap();
staking::transfer(transactor, &dest, value); staking::public::transfer(transactor, &dest, value);
} }
Function::SessionSetKey => { Function::SessionSetKey => {
let session = params.read().unwrap(); let session = params.read().unwrap();
session::set_key(transactor, &session); session::public::set_key(transactor, &session);
} }
Function::TimestampSet => { Function::TimestampSet => {
let t = params.read().unwrap(); 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);
} }
} }
} }
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Self> {
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<F: Fn(&mut [u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
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<usize> {
let first_part = mem::size_of::<Hash>() + mem::size_of::<BlockNumber>() + mem::size_of::<Hash>() + mem::size_of::<Hash>();
let second_part = <Vec<Vec<u8>>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl NonTrivialSlicable for Header {}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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];
@@ -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 <http://www.gnu.org/licenses/>.
//! 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;
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<InternalFunction> {
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<u8>,
}
impl Slicable for Proposal {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(fill_slice: &F) -> Option<Self> {
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<u8> {
Vec::new()
.join(&(self.function as u8))
.join(&self.input_data)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = mem::size_of::<u8>();
let second_part = <Vec<u8>>::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<u8> = 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::<Vec<u8>>());
let o = Proposal::from_slice(&v).unwrap();
assert_eq!(p, o);
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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);
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<u8>,
}
impl Slicable for Transaction {
fn from_slice(value: &[u8]) -> Option<Self> {
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<F: Fn(&mut [u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.signed)
.join(&self.nonce)
.join(&(self.function as u8))
.join(&self.input_data)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = mem::size_of::<AccountID>() + mem::size_of::<TxOrder>() + mem::size_of::<u8>();
let second_part = <Vec<u8>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl NonTrivialSlicable for Transaction {}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Self> {
let mut reader = StreamReader::new(value);
Some(UncheckedTransaction {
signature: reader.read()?,
transaction: reader.read()?,
})
}
fn set_as_slice<F: Fn(&mut [u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.signature)
.join(&self.transaction)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = mem::size_of::<[u8; 64]>();
let second_part = <Transaction>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl NonTrivialSlicable for UncheckedTransaction {}
@@ -16,8 +16,8 @@
//! Conensus module for runtime; manages the authority set ready for the native code. //! Conensus module for runtime; manages the authority set ready for the native code.
use runtime_support::prelude::*; use runtime_std::prelude::*;
use storable::StorageVec; use support::StorageVec;
use primitives::SessionKey; use primitives::SessionKey;
struct AuthorityStorageVec {} struct AuthorityStorageVec {}
@@ -31,14 +31,18 @@ pub fn authorities() -> Vec<SessionKey> {
AuthorityStorageVec::items() AuthorityStorageVec::items()
} }
/// Set the current set of authorities' session keys. pub mod internal {
/// use super::*;
/// Called by `next_session` only.
pub fn set_authorities(authorities: &[SessionKey]) {
AuthorityStorageVec::set_items(authorities);
}
/// Set a single authority by index. /// Set the current set of authorities' session keys.
pub fn set_authority(index: u32, key: &SessionKey) { ///
AuthorityStorageVec::set_item(index, key); /// 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);
}
} }
@@ -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 <http://www.gnu.org/licenses/>.
//! 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::<Proposal>(CURRENT_PROPOSAL) {
let approvals_required = approvals_required();
let approved = session::validators().into_iter()
.filter_map(|v| storage::take::<bool>(&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);
});
}
}
@@ -26,5 +26,8 @@ pub mod staking;
pub mod timestamp; pub mod timestamp;
#[allow(unused)] #[allow(unused)]
pub mod session; pub mod session;
#[allow(unused)]
pub mod governance;
// TODO: governance, polkadao // TODO: polkadao
// TODO: parachains
@@ -17,119 +17,134 @@
//! Session manager: is told the validators and allows them to manage their session keys for the //! Session manager: is told the validators and allows them to manage their session keys for the
//! consensus module. //! consensus module.
use runtime_support::prelude::*; use runtime_std::prelude::*;
use keyedvec::KeyedVec; use codec::KeyedVec;
use storable::{kill, Storable, StorageVec}; use support::{storage, StorageVec};
use primitives::{AccountID, SessionKey, BlockNumber}; use primitives::{AccountID, SessionKey, BlockNumber};
use runtime::{system, staking, consensus}; 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 {} struct ValidatorStorageVec {}
impl StorageVec for ValidatorStorageVec { impl StorageVec for ValidatorStorageVec {
type Item = AccountID; type Item = AccountID;
const PREFIX: &'static[u8] = b"ses:val:"; 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. /// Get the current set of authorities. These are the session keys.
pub fn validators() -> Vec<AccountID> { pub fn validators() -> Vec<AccountID> {
ValidatorStorageVec::items() 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. /// The number of blocks in each session.
pub fn length() -> BlockNumber { 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. /// The current era index.
pub fn current_index() -> BlockNumber { pub fn current_index() -> BlockNumber {
Storable::lookup_default(b"ses:ind") storage::get_or(CURRENT_INDEX, 0)
}
/// Set the current era index.
pub fn set_current_index(new: BlockNumber) {
new.store(b"ses:ind");
} }
/// The block number at which the era length last changed. /// The block number at which the era length last changed.
pub fn last_length_change() -> BlockNumber { 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 mod public {
pub fn set_length(new: BlockNumber) { use super::*;
new.store(b"ses:nln");
}
/// Hook to be called after transaction processing. /// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
pub fn check_rotate_session() { /// session.
// do this last, after the staking system has had chance to switch out the authorities for the pub fn set_key(validator: &AccountID, key: &SessionKey) {
// new set. // set new value for next session
// check block number and call next_session if necessary. storage::put(&validator.to_keyed_vec(NEXT_KEY_FOR), key);
if (system::block_number() - last_length_change()) % length() == 0 {
rotate_session();
} }
} }
// 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. /// Move onto next session: register the new authority set.
fn rotate_session() { fn rotate_session() {
// Increment current session index. // Increment current session index.
set_current_index(current_index() + 1); storage::put(CURRENT_INDEX, &(current_index() + 1));
// Enact era length change. // Enact era length change.
if let Some(next_len) = u64::lookup(b"ses:nln") { if let Some(next_len) = storage::get::<u64>(NEXT_SESSION_LENGTH) {
next_len.store(b"ses:len"); storage::put(SESSION_LENGTH, &next_len);
system::block_number().store(b"ses:llc"); storage::put(LAST_LENGTH_CHANGE, &system::block_number());
kill(b"ses:nln"); storage::kill(NEXT_SESSION_LENGTH);
} }
// Update any changes in session keys. // Update any changes in session keys.
validators().iter().enumerate().for_each(|(i, v)| { validators().iter().enumerate().for_each(|(i, v)| {
let k = v.to_keyed_vec(b"ses:nxt:"); let k = v.to_keyed_vec(NEXT_KEY_FOR);
if let Some(n) = Storable::lookup(&k) { if let Some(n) = storage::take(&k) {
consensus::set_authority(i as u32, &n); consensus::internal::set_authority(i as u32, &n);
kill(&k);
} }
}); });
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use runtime_support::{with_externalities, twox_128}; use super::*;
use keyedvec::KeyedVec; use super::public::*;
use joiner::Joiner; use super::privileged::*;
use testing::{one, two, TestExternalities}; 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 primitives::AccountID;
use runtime::{consensus, session}; use runtime::{consensus, session};
use environment::with_env;
fn simple_setup() -> TestExternalities { fn simple_setup() -> TestExternalities {
TestExternalities { storage: map![ 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, ...) // the validators (10, 20, ...)
twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32), 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(&0u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![10; 32],
twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32], twox_128(&1u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![20; 32],
// initial session keys (11, 21, ...) // initial session keys (11, 21, ...)
twox_128(b"con:aut:len").to_vec() => vec![].join(&2u32), 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], 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(); let mut t = simple_setup();
with_externalities(&mut t, || { with_externalities(&mut t, || {
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
assert_eq!(session::length(), 2u64); assert_eq!(length(), 2u64);
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); assert_eq!(validators(), vec![[10u8; 32], [20u8; 32]]);
}); });
} }
@@ -153,48 +168,48 @@ mod tests {
with_externalities(&mut t, || { with_externalities(&mut t, || {
// Block 1: Change to length 3; no visible change. // Block 1: Change to length 3; no visible change.
with_env(|e| e.block_number = 1); with_env(|e| e.block_number = 1);
session::set_length(3); set_length(3);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 2); assert_eq!(length(), 2);
assert_eq!(session::current_index(), 0); assert_eq!(current_index(), 0);
// Block 2: Length now changed to 3. Index incremented. // Block 2: Length now changed to 3. Index incremented.
with_env(|e| e.block_number = 2); with_env(|e| e.block_number = 2);
session::set_length(3); set_length(3);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 3); assert_eq!(length(), 3);
assert_eq!(session::current_index(), 1); assert_eq!(current_index(), 1);
// Block 3: Length now changed to 3. Index incremented. // Block 3: Length now changed to 3. Index incremented.
with_env(|e| e.block_number = 3); with_env(|e| e.block_number = 3);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 3); assert_eq!(length(), 3);
assert_eq!(session::current_index(), 1); assert_eq!(current_index(), 1);
// Block 4: Change to length 2; no visible change. // Block 4: Change to length 2; no visible change.
with_env(|e| e.block_number = 4); with_env(|e| e.block_number = 4);
session::set_length(2); set_length(2);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 3); assert_eq!(length(), 3);
assert_eq!(session::current_index(), 1); assert_eq!(current_index(), 1);
// Block 5: Length now changed to 2. Index incremented. // Block 5: Length now changed to 2. Index incremented.
with_env(|e| e.block_number = 5); with_env(|e| e.block_number = 5);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 2); assert_eq!(length(), 2);
assert_eq!(session::current_index(), 2); assert_eq!(current_index(), 2);
// Block 6: No change. // Block 6: No change.
with_env(|e| e.block_number = 6); with_env(|e| e.block_number = 6);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 2); assert_eq!(length(), 2);
assert_eq!(session::current_index(), 2); assert_eq!(current_index(), 2);
// Block 7: Next index. // Block 7: Next index.
with_env(|e| e.block_number = 7); with_env(|e| e.block_number = 7);
session::check_rotate_session(); check_rotate_session();
assert_eq!(session::length(), 2); assert_eq!(length(), 2);
assert_eq!(session::current_index(), 3); assert_eq!(current_index(), 3);
}); });
} }
@@ -204,25 +219,25 @@ mod tests {
with_externalities(&mut t, || { with_externalities(&mut t, || {
// Block 1: No change // Block 1: No change
with_env(|e| e.block_number = 1); with_env(|e| e.block_number = 1);
session::check_rotate_session(); check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 2: Session rollover, but no change. // Block 2: Session rollover, but no change.
with_env(|e| e.block_number = 2); with_env(|e| e.block_number = 2);
session::check_rotate_session(); check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 3: Set new key for validator 2; no visible change. // Block 3: Set new key for validator 2; no visible change.
with_env(|e| e.block_number = 3); 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]]); 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]]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 4: Session rollover, authority 2 changes. // Block 4: Session rollover, authority 2 changes.
with_env(|e| e.block_number = 4); with_env(|e| e.block_number = 4);
session::check_rotate_session(); check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]); assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]);
}); });
} }
@@ -16,11 +16,12 @@
//! Staking manager: Handles balances and periodically determines the best set of validators. //! Staking manager: Handles balances and periodically determines the best set of validators.
use runtime_support::prelude::*; use runtime_std::prelude::*;
use keyedvec::KeyedVec; use runtime_std::cell::RefCell;
use storable::{Storable, StorageVec}; use codec::KeyedVec;
use support::{storage, StorageVec};
use primitives::{BlockNumber, AccountID}; use primitives::{BlockNumber, AccountID};
use runtime::{system, session}; use runtime::{system, session, governance};
/// The balance of an account. /// The balance of an account.
pub type Balance = u64; pub type Balance = u64;
@@ -31,23 +32,26 @@ pub type Bondage = u64;
struct IntentionStorageVec {} struct IntentionStorageVec {}
impl StorageVec for IntentionStorageVec { impl StorageVec for IntentionStorageVec {
type Item = AccountID; 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: const BONDING_DURATION: &[u8] = b"sta:loc";
// - n | n <= current_era(): inactive: free to be transferred. const VALIDATOR_COUNT: &[u8] = b"sta:vac";
// - ~0: active: currently representing a validator. const SESSIONS_PER_ERA: &[u8] = b"sta:spe";
// - n | n > current_era(): deactivating: recently representing a validator and not yet const NEXT_SESSIONS_PER_ERA: &[u8] = b"sta:nse";
// ready for transfer. 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. /// The length of the bonding duration in eras.
pub fn bonding_duration() -> BlockNumber { 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. /// The length of a staking era in sessions.
pub fn validator_count() -> usize { pub fn validator_count() -> usize {
u32::lookup_default(b"sta:vac") as usize storage::get_or_default::<u32>(VALIDATOR_COUNT) as usize
} }
/// The length of a staking era in blocks. /// 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. /// The length of a staking era in sessions.
pub fn sessions_per_era() -> BlockNumber { pub fn sessions_per_era() -> BlockNumber {
Storable::lookup_default(b"sta:spe") storage::get_or_default(SESSIONS_PER_ERA)
} }
/// The current era index. /// The current era index.
pub fn current_era() -> BlockNumber { 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. /// The block number at which the era length last changed.
pub fn last_era_length_change() -> BlockNumber { 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. /// The balance of a given account.
pub fn balance(who: &AccountID) -> Balance { 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. /// The liquidity-state of a given account.
pub fn bondage(who: &AccountID) -> Bondage { 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. // Each identity's stake may be in one of three bondage states, given by an integer:
pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { // - n | n <= current_era(): inactive: free to be transferred.
let from_key = transactor.to_keyed_vec(b"sta:bal:"); // - ~0: active: currently representing a validator.
let from_balance = Balance::lookup_default(&from_key); // - n | n > current_era(): deactivating: recently representing a validator and not yet
assert!(from_balance >= value); // ready for transfer.
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);
}
/// Declare the desire to stake for the transactor. pub mod public {
/// use super::*;
/// 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:"));
}
/// Retract the desire to stake for the transactor. /// Transfer some unlocked staking balance to another staker.
/// pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) {
/// Effects will be felt at the beginning of the next era. let from_key = transactor.to_keyed_vec(BALANCE_OF);
pub fn unstake(transactor: &AccountID) { let from_balance = storage::get_or_default::<Balance>(&from_key);
let mut intentions = IntentionStorageVec::items(); assert!(from_balance >= value);
if let Some(position) = intentions.iter().position(|t| t == transactor) { let to_key = dest.to_keyed_vec(BALANCE_OF);
intentions.swap_remove(position); let to_balance: Balance = storage::get_or_default(&to_key);
} else { assert!(bondage(transactor) <= bondage(dest));
panic!("Cannot unstake if not already staked."); 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. /// Declare the desire to stake for the transactor.
pub fn check_new_era() { ///
// check block number and call new_era if necessary. /// Effects will be felt at the beginning of the next era.
if (system::block_number() - last_era_length_change()) % era_length() == 0 { pub fn stake(transactor: &AccountID) {
new_era(); 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. /// 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() { fn new_era() {
// Inform governance module that it's the end of an era
governance::internal::end_of_an_era();
// Increment current era. // Increment current era.
(current_era() + 1).store(b"sta:era"); storage::put(CURRENT_ERA, &(current_era() + 1));
// Enact era length change. // 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() { if next_spe > 0 && next_spe != sessions_per_era() {
next_spe.store(b"sta:spe"); storage::put(SESSIONS_PER_ERA, &next_spe);
system::block_number().store(b"sta:lec"); storage::put(LAST_ERA_LENGTH_CHANGE, &system::block_number());
} }
// TODO: evaluate desired staking amounts and nominations and optimise to find the best // evaluate desired staking amounts and nominations and optimise to find the best
// combination of validators, then use session::set_validators(). // 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 // for now, this just orders would-be stakers by their balances and chooses the top-most
// validator_count() of them. // validator_count() of them.
let mut intentions = IntentionStorageVec::items() let mut intentions = IntentionStorageVec::items()
@@ -153,7 +191,7 @@ fn new_era() {
.map(|v| (balance(&v), v)) .map(|v| (balance(&v), v))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1)); intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1));
session::set_validators( session::internal::set_validators(
&intentions.into_iter() &intentions.into_iter()
.map(|(_, v)| v) .map(|(_, v)| v)
.take(validator_count()) .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)] #[cfg(test)]
mod tests { mod tests {
use runtime_support::{with_externalities, twox_128}; use super::*;
use keyedvec::KeyedVec; use super::internal::*;
use joiner::Joiner; use super::public::*;
use testing::{one, two, TestExternalities}; 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 primitives::AccountID;
use runtime::{staking, session}; use runtime::{staking, session};
use environment::with_env;
#[test] #[test]
fn staking_should_work() { fn staking_should_work() {
@@ -188,64 +224,64 @@ mod tests {
twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32), 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(&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(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32],
twox_128(b"sta:spe").to_vec() => vec![].join(&2u64), twox_128(SESSIONS_PER_ERA).to_vec() => vec![].join(&2u64),
twox_128(b"sta:vac").to_vec() => vec![].join(&2u32), twox_128(VALIDATOR_COUNT).to_vec() => vec![].join(&2u32),
twox_128(b"sta:loc").to_vec() => vec![].join(&3u64), twox_128(BONDING_DURATION).to_vec() => vec![].join(&3u64),
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&10u64), twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&10u64),
twox_128(&two.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&20u64), twox_128(&two.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&20u64),
twox_128(&three.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&30u64), twox_128(&three.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&30u64),
twox_128(&four.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&40u64) twox_128(&four.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&40u64)
], }; ], };
with_externalities(&mut t, || { with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 2u64); assert_eq!(era_length(), 2u64);
assert_eq!(staking::validator_count(), 2usize); assert_eq!(validator_count(), 2usize);
assert_eq!(staking::bonding_duration(), 3u64); assert_eq!(bonding_duration(), 3u64);
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
// Block 1: Add three validators. No obvious change. // Block 1: Add three validators. No obvious change.
with_env(|e| e.block_number = 1); with_env(|e| e.block_number = 1);
staking::stake(&one); stake(&one);
staking::stake(&two); stake(&two);
staking::stake(&four); stake(&four);
staking::check_new_era(); check_new_era();
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]); assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
// Block 2: New validator set now. // Block 2: New validator set now.
with_env(|e| e.block_number = 2); with_env(|e| e.block_number = 2);
staking::check_new_era(); check_new_era();
assert_eq!(session::validators(), vec![four.clone(), two.clone()]); assert_eq!(session::validators(), vec![four.clone(), two.clone()]);
// Block 3: Unstake highest, introduce another staker. No change yet. // Block 3: Unstake highest, introduce another staker. No change yet.
with_env(|e| e.block_number = 3); with_env(|e| e.block_number = 3);
staking::stake(&three); stake(&three);
staking::unstake(&four); unstake(&four);
staking::check_new_era(); check_new_era();
// Block 4: New era - validators change. // Block 4: New era - validators change.
with_env(|e| e.block_number = 4); with_env(|e| e.block_number = 4);
staking::check_new_era(); check_new_era();
assert_eq!(session::validators(), vec![three.clone(), two.clone()]); assert_eq!(session::validators(), vec![three.clone(), two.clone()]);
// Block 5: Transfer stake from highest to lowest. No change yet. // Block 5: Transfer stake from highest to lowest. No change yet.
with_env(|e| e.block_number = 5); with_env(|e| e.block_number = 5);
staking::transfer(&four, &one, 40); transfer(&four, &one, 40);
staking::check_new_era(); check_new_era();
// Block 6: Lowest now validator. // Block 6: Lowest now validator.
with_env(|e| e.block_number = 6); with_env(|e| e.block_number = 6);
staking::check_new_era(); check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]); assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 7: Unstake three. No change yet. // Block 7: Unstake three. No change yet.
with_env(|e| e.block_number = 7); with_env(|e| e.block_number = 7);
staking::unstake(&three); unstake(&three);
staking::check_new_era(); check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]); assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 8: Back to one and two. // Block 8: Back to one and two.
with_env(|e| e.block_number = 8); with_env(|e| e.block_number = 8);
staking::check_new_era(); check_new_era();
assert_eq!(session::validators(), vec![one.clone(), two.clone()]); assert_eq!(session::validators(), vec![one.clone(), two.clone()]);
}); });
} }
@@ -254,63 +290,63 @@ mod tests {
fn staking_eras_work() { fn staking_eras_work() {
let mut t = TestExternalities { storage: map![ let mut t = TestExternalities { storage: map![
twox_128(b"ses:len").to_vec() => vec![].join(&1u64), 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, || { with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 2u64); assert_eq!(era_length(), 2u64);
assert_eq!(staking::sessions_per_era(), 2u64); assert_eq!(sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64); assert_eq!(last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 0u64); assert_eq!(current_era(), 0u64);
// Block 1: No change. // Block 1: No change.
with_env(|e| e.block_number = 1); with_env(|e| e.block_number = 1);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 2u64); assert_eq!(sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64); assert_eq!(last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 0u64); assert_eq!(current_era(), 0u64);
// Block 2: Simple era change. // Block 2: Simple era change.
with_env(|e| e.block_number = 2); with_env(|e| e.block_number = 2);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 2u64); assert_eq!(sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64); assert_eq!(last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 1u64); assert_eq!(current_era(), 1u64);
// Block 3: Schedule an era length change; no visible changes. // Block 3: Schedule an era length change; no visible changes.
with_env(|e| e.block_number = 3); with_env(|e| e.block_number = 3);
staking::set_sessions_per_era(3); set_sessions_per_era(3);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 2u64); assert_eq!(sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64); assert_eq!(last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 1u64); assert_eq!(current_era(), 1u64);
// Block 4: Era change kicks in. // Block 4: Era change kicks in.
with_env(|e| e.block_number = 4); with_env(|e| e.block_number = 4);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64); assert_eq!(sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64); assert_eq!(last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 2u64); assert_eq!(current_era(), 2u64);
// Block 5: No change. // Block 5: No change.
with_env(|e| e.block_number = 5); with_env(|e| e.block_number = 5);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64); assert_eq!(sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64); assert_eq!(last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 2u64); assert_eq!(current_era(), 2u64);
// Block 6: No change. // Block 6: No change.
with_env(|e| e.block_number = 6); with_env(|e| e.block_number = 6);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64); assert_eq!(sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64); assert_eq!(last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 2u64); assert_eq!(current_era(), 2u64);
// Block 7: Era increment. // Block 7: Era increment.
with_env(|e| e.block_number = 7); with_env(|e| e.block_number = 7);
staking::check_new_era(); check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64); assert_eq!(sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64); assert_eq!(last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 3u64); assert_eq!(current_era(), 3u64);
}); });
} }
@@ -320,12 +356,12 @@ mod tests {
let two = two(); let two = two();
let mut t = TestExternalities { storage: map![ 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, || { with_externalities(&mut t, || {
assert_eq!(staking::balance(&one), 42); assert_eq!(balance(&one), 42);
assert_eq!(staking::balance(&two), 0); assert_eq!(balance(&two), 0);
}); });
} }
@@ -335,13 +371,13 @@ mod tests {
let two = two(); let two = two();
let mut t = TestExternalities { storage: map![ 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, || { with_externalities(&mut t, || {
staking::transfer(&one, &two, 69); transfer(&one, &two, 69);
assert_eq!(staking::balance(&one), 42); assert_eq!(balance(&one), 42);
assert_eq!(staking::balance(&two), 69); assert_eq!(balance(&two), 69);
}); });
} }
@@ -352,12 +388,12 @@ mod tests {
let two = two(); let two = two();
let mut t = TestExternalities { storage: map![ 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, || { with_externalities(&mut t, || {
staking::stake(&one); stake(&one);
staking::transfer(&one, &two, 69); transfer(&one, &two, 69);
}); });
} }
} }
@@ -17,14 +17,16 @@
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code //! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
//! and depositing logs. //! and depositing logs.
use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder, Hashable}; use runtime_std::prelude::*;
use runtime_support::mem; use runtime_std::{mem, print};
use runtime_support::prelude::*; use codec::KeyedVec;
use storable::Storable; use support::{Hashable, storage, with_env};
use keyedvec::KeyedVec; use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder};
use environment::with_env;
use runtime::{staking, session}; 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`. /// The current block number being processed. Set by `execute_block`.
pub fn block_number() -> BlockNumber { pub fn block_number() -> BlockNumber {
with_env(|e| e.block_number) 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). /// Get the block hash of a given block (uses storage).
pub fn block_hash(number: BlockNumber) -> Hash { 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 mod privileged {
pub fn deposit_log(log: &[u8]) { use super::*;
with_env(|e| {
assert_eq!(log, &e.digest.logs[e.next_log_index][..]); /// Set the new code.
e.next_log_index += 1; pub fn set_code(new: &[u8]) {
}); storage::put_raw(CODE, new);
}
} }
pub fn execute_block(mut block: Block) { pub mod internal {
// populate environment from header. use super::*;
with_env(|e| {
e.block_number = block.header.number;
mem::swap(&mut e.digest, &mut block.header.digest);
e.next_log_index = 0;
});
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. /// Actually execute all transitioning for `block`.
assert!( pub fn execute_block(mut block: Block) {
header.number > 0 && block_hash(header.number - 1) == header.parent_hash, // populate environment from header.
"Parent hash should be valid." 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. let ref header = block.header;
// this requires non-trivial changes to the externals API or compiling trie rooting into wasm
// so will wait until a little later.
// store the header hash in storage. // check parent_hash is correct.
let header_hash_key = header.number.to_keyed_vec(b"sys:old:"); assert!(
header.blake2_256().store(&header_hash_key); header.number > 0 && block_hash(header.number - 1) == header.parent_hash,
"Parent hash should be valid."
);
// execute transactions // TODO: check transaction trie root represents the transactions.
block.transactions.iter().for_each(execute_transaction); // 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(); // store the header hash in storage.
session::check_rotate_session(); let header_hash_key = header.number.to_keyed_vec(BLOCK_HASH_AT);
storage::put(&header_hash_key, &header.blake2_256());
// any final checks // execute transactions
final_checks(&block); block.transactions.iter().for_each(execute_transaction);
// TODO: check storage root. staking::internal::check_new_era();
// this requires non-trivial changes to the externals API or compiling trie rooting into wasm session::internal::check_rotate_session();
// so will wait until a little later.
}
/// Execute a given transaction. // any final checks
pub fn execute_transaction(utx: &UncheckedTransaction) { final_checks(&block);
// Verify the signature is good.
assert!(utx.ed25519_verify(), "All transactions should be properly signed");
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 /// Execute a given transaction.
let nonce_key = tx.signed.to_keyed_vec(b"sys:non:"); pub fn execute_transaction(utx: &UncheckedTransaction) {
let expected_nonce: TxOrder = Storable::lookup_default(&nonce_key); // Verify the signature is good.
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); assert!(utx.ed25519_verify(), "All transactions should be properly signed");
// increment nonce in storage let ref tx = utx.transaction;
(expected_nonce + 1).store(&nonce_key);
// decode parameters and dispatch // check nonce
tx.function.dispatch(&tx.signed, &tx.input_data); 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. // increment nonce in storage
pub fn set_code(new: &[u8]) { storage::put(&nonce_key, &(expected_nonce + 1));
new.store(b":code");
// decode parameters and dispatch
tx.function.dispatch(&tx.signed, &tx.input_data);
}
} }
fn final_checks(_block: &Block) { fn final_checks(_block: &Block) {
@@ -113,15 +124,14 @@ fn final_checks(_block: &Block) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use joiner::Joiner; use super::*;
use function::Function; use super::internal::*;
use keyedvec::KeyedVec;
use slicable::Slicable; use runtime_std::{with_externalities, twox_128};
use runtime_support::{with_externalities, twox_128}; use codec::{Joiner, KeyedVec, Slicable};
use primitives::{UncheckedTransaction, Transaction}; use support::{StaticHexInto, TestExternalities, HexDisplay, one, two};
use statichex::StaticHexInto; use primitives::{UncheckedTransaction, Transaction, Function};
use runtime::{system, staking}; use runtime::staking;
use testing::{TestExternalities, HexDisplay, one, two};
#[test] #[test]
fn staking_balance_transfer_dispatch_works() { fn staking_balance_transfer_dispatch_works() {
@@ -147,7 +157,7 @@ mod tests {
println!("tx is {}", HexDisplay::from(&tx.transaction.to_vec())); println!("tx is {}", HexDisplay::from(&tx.transaction.to_vec()));
with_externalities(&mut t, || { with_externalities(&mut t, || {
system::execute_transaction(&tx); execute_transaction(&tx);
assert_eq!(staking::balance(&one), 42); assert_eq!(staking::balance(&one), 42);
assert_eq!(staking::balance(&two), 69); assert_eq!(staking::balance(&two), 69);
}); });
@@ -16,39 +16,47 @@
//! Timestamp manager: just handles the current timestamp. //! Timestamp manager: just handles the current timestamp.
use storable::Storable; use support::storage;
const CURRENT_TIMESTAMP: &[u8] = b"tim:val";
/// Representation of a time. /// Representation of a time.
pub type Timestamp = u64; pub type Timestamp = u64;
/// Get the current time. /// Get the current time.
pub fn get() -> Timestamp { pub fn get() -> Timestamp {
Storable::lookup_default(b"tim:val") storage::get_or_default(CURRENT_TIMESTAMP)
} }
/// Set the current time. pub mod public {
pub fn set(now: Timestamp) { use super::*;
now.store(b"tim:val")
/// Set the current time.
pub fn set(now: Timestamp) {
storage::put(CURRENT_TIMESTAMP, &now);
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use joiner::Joiner; use super::*;
use keyedvec::KeyedVec; use super::public::*;
use runtime_support::{with_externalities, twox_128};
use runtime_std::{with_externalities, twox_128};
use runtime::timestamp; use runtime::timestamp;
use testing::TestExternalities; use codec::{Joiner, KeyedVec};
use support::TestExternalities;
#[test] #[test]
fn timestamp_works() { fn timestamp_works() {
let mut t = TestExternalities { storage: map![ 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, || { with_externalities(&mut t, || {
assert_eq!(timestamp::get(), 42); assert_eq!(get(), 42);
timestamp::set(69); set(69);
assert_eq!(timestamp::get(), 69); assert_eq!(get(), 69);
}); });
} }
} }
@@ -16,10 +16,10 @@
//! Environment API: Allows certain information to be accessed throughout the runtime. //! Environment API: Allows certain information to be accessed throughout the runtime.
use runtime_support::boxed::Box; use runtime_std::boxed::Box;
use runtime_support::mem; use runtime_std::mem;
use runtime_support::cell::RefCell; use runtime_std::cell::RefCell;
use runtime_support::rc::Rc; use runtime_std::rc::Rc;
use primitives::{BlockNumber, Digest}; use primitives::{BlockNumber, Digest};
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<T: Slicable> 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())
}
}
@@ -16,13 +16,19 @@
//! Support code for the runtime. //! Support code for the runtime.
pub mod primitives; mod environment;
pub mod function; pub mod storage;
pub mod environment; mod hashable;
pub mod storable; #[cfg(feature = "with-std")]
mod statichex;
#[cfg(test)]
pub mod statichex;
#[cfg(test)]
#[macro_use] #[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};
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Vec<u8>>,
}
#[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<Self> {
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<F: FnOnce(&mut [u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
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<usize> {
let first_part = mem::size_of::<Hash>() + mem::size_of::<BlockNumber>() + mem::size_of::<Hash>() + mem::size_of::<Hash>();
let second_part = <Vec<Vec<u8>>>::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<u8>,
}
impl Slicable for Transaction {
fn from_slice(value: &[u8]) -> Option<Self> {
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<F: FnOnce(&mut [u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.signed)
.join(&self.nonce)
.join(&(self.function as u8))
.join(&self.input_data)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = mem::size_of::<AccountID>() + mem::size_of::<TxOrder>() + mem::size_of::<u8>();
let second_part = <Vec<u8>>::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<T: Slicable> 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<Self> {
let mut reader = StreamReader::new(value);
Some(UncheckedTransaction {
signature: reader.read()?,
transaction: reader.read()?,
})
}
fn set_as_slice<F: FnOnce(&mut [u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.signature)
.join(&self.transaction)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = mem::size_of::<[u8; 64]>();
let second_part = <Transaction>::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<UncheckedTransaction>,
}
impl Slicable for Block {
fn from_slice(value: &[u8]) -> Option<Self> {
let mut reader = StreamReader::new(value);
Some(Block {
header: reader.read()?,
transactions: reader.read()?,
})
}
fn set_as_slice<F: FnOnce(&mut [u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.header)
.join(&self.transactions)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = Header::size_of(data)?;
let second_part = <Vec<Transaction>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl NonTrivialSlicable for Block {}
impl<T: Slicable> NonTrivialSlicable for Vec<T> where Vec<T>: Slicable {}
impl<T: NonTrivialSlicable> Slicable for Vec<T> {
fn from_slice(value: &[u8]) -> Option<Self> {
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<F: FnOnce(&mut [u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
let vecs = self.iter().map(Slicable::to_vec).collect::<Vec<_>>();
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<usize> {
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);
}
}
@@ -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<u8> {
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 /// Trait to allow converting from itself (only implemented for a static str) into some useful
/// type (which must implement `StaticHexConversion`). /// type (which must implement `StaticHexConversion`).
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Self> 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<T: Default + EndianSensitive> Storable for T {
fn lookup(key: &[u8]) -> Option<Self> {
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<Self::Item> {
(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))
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
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<T: Slicable + Sized + 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<T: Slicable + Sized>(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: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
get(key).unwrap_or_else(default_value)
}
/// Please `value` in storage under `key`.
pub fn put<T: Slicable>(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<T: Slicable>(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<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
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<T: Slicable + Sized + 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<T: Slicable + Sized>(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: Slicable + Sized, F: FnOnce() -> 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<u8> {
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<Self::Item> {
(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::<Vec<u8>>(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<u8> = 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);
});
}
}
@@ -16,10 +16,10 @@
//! Testing helpers. //! Testing helpers.
use runtime_support::{NoError, Externalities};
use std::collections::HashMap; use std::collections::HashMap;
use runtime_std::{Externalities, ExternalitiesError};
use primitives::AccountID; use primitives::AccountID;
use statichex::StaticHexInto; use super::statichex::StaticHexInto;
#[derive(Debug, Default)] #[derive(Debug, Default)]
/// Simple externaties implementation. /// Simple externaties implementation.
@@ -29,9 +29,7 @@ pub struct TestExternalities {
} }
impl Externalities for TestExternalities { impl Externalities for TestExternalities {
type Error = NoError; fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
} }
@@ -8,6 +8,7 @@ extern "C" {
fn ext_memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; 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_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_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_malloc(size: usize) -> *mut u8;
fn ext_free(ptr: *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) 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 /// memmove extern
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
@@ -1,5 +1,5 @@
[package] [package]
name = "runtime-support" name = "runtime-std"
version = "0.1.0" version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
@@ -1,9 +1,9 @@
#![no_std] #![no_std]
#![feature(lang_items)] #![feature(lang_items)]
#![feature(core_intrinsics)]
#![feature(alloc)] #![feature(alloc)]
#![cfg_attr(feature = "strict", deny(warnings))] #![cfg_attr(feature = "strict", deny(warnings))]
#![feature(alloc)]
extern crate alloc; extern crate alloc;
pub use alloc::vec; pub use alloc::vec;
@@ -26,16 +26,22 @@ extern crate pwasm_alloc;
#[lang = "panic_fmt"] #[lang = "panic_fmt"]
#[no_mangle] #[no_mangle]
pub fn panic_fmt() -> ! { pub extern fn panic_fmt(_fmt: ::core::fmt::Arguments, _file: &'static str, _line: u32, _col: u32) {
loop {} 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" { 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_print_num(value: u64);
fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); 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_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_chain_id() -> u64;
fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_128(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<u8> { pub fn storage(key: &[u8]) -> Vec<u8> {
let mut length: u32 = 0; let mut length: u32 = 0;
unsafe { 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) Vec::from_raw_parts(ptr, length as usize, length as usize)
} }
} }
@@ -54,15 +60,15 @@ pub fn storage(key: &[u8]) -> Vec<u8> {
pub fn set_storage(key: &[u8], value: &[u8]) { pub fn set_storage(key: &[u8], value: &[u8]) {
unsafe { unsafe {
ext_set_storage( ext_set_storage(
&key[0] as *const u8, key.len() as u32, key.as_ptr(), key.len() as u32,
&value[0] as *const u8, value.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 { 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. /// Conduct a 256-bit Blake2 hash.
pub fn blake2_256(data: &[u8]) -> [u8; 32] { pub fn blake2_256(data: &[u8]) -> [u8; 32] {
let mut result: [u8; 32] = Default::default();
// guaranteed to write into result.
unsafe { unsafe {
let mut result: [u8; 32] = Default::default(); ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
// guaranteed to write into result.
ext_blake2_256(&data[0], data.len() as u32, &mut result[0]);
result
} }
result
} }
/// Conduct four XX hashes to give a 256-bit result. /// Conduct four XX hashes to give a 256-bit result.
pub fn twox_256(data: &[u8]) -> [u8; 32] { pub fn twox_256(data: &[u8]) -> [u8; 32] {
let mut result: [u8; 32] = Default::default();
// guaranteed to write into result.
unsafe { unsafe {
let mut result: [u8; 32] = Default::default(); ext_twox_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
// guaranteed to write into result.
ext_twox_256(&data[0], data.len() as u32, &mut result[0]);
result
} }
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] { pub fn twox_128(data: &[u8]) -> [u8; 16] {
let mut result: [u8; 16] = Default::default();
// guaranteed to write into result.
unsafe { unsafe {
let mut result: [u8; 16] = Default::default(); ext_twox_128(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
// guaranteed to write into result.
ext_twox_128(&data[0], data.len() as u32, &mut result[0]);
result
} }
result
} }
/// Verify a ed25519 signature. /// Verify a ed25519 signature.
pub fn ed25519_verify(sig: &[u8], msg: &[u8], pubkey: &[u8]) -> bool { pub fn ed25519_verify(sig: &[u8], msg: &[u8], pubkey: &[u8]) -> bool {
sig.len() != 64 || pubkey.len() != 32 || unsafe { sig.len() == 64 && pubkey.len() == 32 && unsafe {
ext_ed25519_verify(&msg[0], msg.len() as u32, &sig[0], &pubkey[0]) ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ptr())
} == 0 } == 0
} }
@@ -117,7 +123,15 @@ pub trait Printable {
impl<'a> Printable for &'a [u8] { impl<'a> Printable for &'a [u8] {
fn print(self) { fn print(self) {
unsafe { 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] #[no_mangle]
pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { pub fn $name(input_data: *mut u8, input_len: usize) -> u64 {
let input = unsafe { let input = if input_len == 0 {
$crate::vec::Vec::from_raw_parts(input_data, input_len, input_len) &[0u8; 0]
} else {
unsafe {
$crate::slice::from_raw_parts(input_data, input_len)
}
}; };
let output = super::$name(input); 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)
} }
)* )*
} }
+1 -1
View File
@@ -7,4 +7,4 @@ authors = ["Parity Technologies <admin@parity.io>"]
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
runtime-support = { path = "../support", version = "0.1" } runtime-std = { path = "../std", version = "0.1" }
+28 -12
View File
@@ -7,40 +7,56 @@ extern crate alloc;
use alloc::vec::Vec; use alloc::vec::Vec;
#[macro_use] #[macro_use]
extern crate runtime_support; extern crate runtime_std;
use runtime_support::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify}; use runtime_std::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify};
fn test_blake2_256(input: Vec<u8>) -> Vec<u8> { fn test_blake2_256(input: &[u8]) -> Vec<u8> {
blake2_256(&input).to_vec() blake2_256(&input).to_vec()
} }
fn test_twox_256(input: Vec<u8>) -> Vec<u8> { fn test_twox_256(input: &[u8]) -> Vec<u8> {
twox_256(&input).to_vec() twox_256(&input).to_vec()
} }
fn test_twox_128(input: Vec<u8>) -> Vec<u8> { fn test_twox_128(input: &[u8]) -> Vec<u8> {
twox_128(&input).to_vec() twox_128(&input).to_vec()
} }
fn test_ed25519_verify(input: Vec<u8>) -> Vec<u8> { fn test_ed25519_verify(input: &[u8]) -> Vec<u8> {
let sig = &input[0..64]; let sig = &input[0..64];
let pubkey = &input[64..96]; let pubkey = &input[64..96];
let msg = b"all ok!"; let msg = b"all ok!";
[ed25519_verify(sig, &msg[..], pubkey) as u8].to_vec() [ed25519_verify(sig, &msg[..], pubkey) as u8].to_vec()
} }
fn test_data_in(input: Vec<u8>) -> Vec<u8> { fn test_data_in(input: &[u8]) -> Vec<u8> {
print(b"set_storage" as &[u8]); print("set_storage");
set_storage(b"input", &input); set_storage(b"input", &input);
print(b"storage" as &[u8]); print("storage");
let foo = storage(b"foo"); let foo = storage(b"foo");
print(b"set_storage" as &[u8]); print("set_storage");
set_storage(b"baz", &foo); set_storage(b"baz", &foo);
print(b"finished!" as &[u8]); print("finished!");
b"all ok!".to_vec() 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<u8> {
Vec::new()
}
fn test_panic(_input: &[u8]) -> Vec<u8> {
panic!("test panic");
}
fn test_conditional_panic(input: &[u8]) -> Vec<u8> {
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);