mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-23 21:21:06 +00:00
Generated
+5
-2
@@ -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",
|
||||||
|
|||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd wasm-runtime
|
||||||
|
./build.sh
|
||||||
|
cd ..
|
||||||
|
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
-1
@@ -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>"]
|
||||||
|
|
||||||
+56
-10
@@ -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");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+3
-3
@@ -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",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -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 ]]
|
||||||
|
|||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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>>,
|
||||||
|
}
|
||||||
+33
-21
@@ -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)
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -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" }
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user