Introduce basic skeleton for Polkadot runtime. (#32)

* Introduce basic skeleton for Polkador runtime.

* Clean up the runtime skeleton.

* Make initial runtime skeleton compile.

* Compile polkadot-runtime both for Wasm ad native, allowing for testing and direct usage.

* More fleshing out on runtime.

* Update native support.

* Fix warning.

* Update gitignore

* Update path.

* Fix path.

* Remove accidentally committed files.

* Add wasm binaries.

* Fix test.

* Native storage support API.

* Add environmental module

* Add native environment to make native source-code compatible with wasm.

Also tests.

* Finish up & polish environment stuff.

* Avoid using reentrancy issues.

* Add some docs and a test.

* Remove unneeded function.

* Documentation

* Tweak docs

* Remove TODOs.

* Balance transfers + util methods.

* Rejig tests and ensure authorities are addressed consistently.

* Add marshaller for xfer function

* Transaction dispatch test.

* Minor fix.

* Add test for ser/de transaction.

* Add ser/de for header.

* Add tests for header ser/de

* Introduce basic block decoding/execution framework.

* Introduce block decoding/execution framework (p2)

* Big refactor.

* Split out joiner.

* Hide away support modules.

* Fix up wasm runtime.

* use externalities for chain_id

* Clean up (Test)Externalities.

* Repot and introduce keccak-256 external.

* Signing with crypto.

* fix unsafety hole in environmental using function

* Introduce Ed25519 crypto.

* Repotting.

* Add ed25519_verify external.

* Introduce Ed25519 verify as an external.

* fix unsafety hole around unwinding

* Compile fixes.

* use new environmental API

* Tests for ed25519 verify.

* Polish

* Introduce UncheckedTransaction & test.

* Implement basic block and tx processing

* Introduce static hex and valid signature for block test.

* Repot session.

* comments.

* Refactor and timestamp test

* Remove fluff

* Remove fluff.

* Staking eras and tests.

* Implement sessions.

* Polish

* Test sessions.

* Introduce better hashing.

- Blake2 for secure hashing
- XX for fast hashing

* Fix tests.

* Introduce staking.

* Tests for simple staking system.

* Build fix for wasm.

* Fix tests.

* Repotting and docs.

* Docs and licence.

* Documentation.

* Remove superfluous code.

* Remove dummy key.

* Remove other superfluous file.

* Optimise with swap_remove
This commit is contained in:
Gav Wood
2018-01-23 15:24:17 +01:00
committed by Arkadiy Paronyan
parent 28d84d8ac4
commit 3402f169a7
70 changed files with 3578 additions and 282 deletions
+33
View File
@@ -0,0 +1,33 @@
[[package]]
name = "pwasm-alloc"
version = "0.1.0"
dependencies = [
"pwasm-libc 0.1.0",
]
[[package]]
name = "pwasm-libc"
version = "0.1.0"
[[package]]
name = "runtime-polkadot"
version = "0.1.0"
dependencies = [
"runtime-support 0.1.0",
]
[[package]]
name = "runtime-support"
version = "0.1.0"
dependencies = [
"pwasm-alloc 0.1.0",
"pwasm-libc 0.1.0",
]
[[package]]
name = "runtime-test"
version = "0.1.0"
dependencies = [
"runtime-support 0.1.0",
]
+8
View File
@@ -0,0 +1,8 @@
[workspace]
members = [
"test",
"polkadot",
]
[profile.release]
panic = "abort"
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
cargo +nightly build --target=wasm32-unknown-unknown --release
dirs=`find * -maxdepth 0 -type d | grep -v pwasm- | grep -v support`
for i in $dirs
do
if [[ -e $i/Cargo.toml ]]
then
wasm-gc target/wasm32-unknown-unknown/release/runtime_$i.wasm target/wasm32-unknown-unknown/release/runtime_$i.compact.wasm
fi
done
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup update stable
cargo install --git https://github.com/alexcrichton/wasm-gc
@@ -0,0 +1,15 @@
[package]
name = "runtime-polkadot"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
runtime-support = { path = "../support", version = "0.1" }
[features]
default = ["without-std"]
with-std = []
without-std = []
@@ -0,0 +1,50 @@
// 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/>.
//! Endian manager.
/// Trait to allow conversion to a know endian representation when sensitive.
pub trait EndianSensitive: Sized {
fn to_le(self) -> Self { self }
fn to_be(self) -> Self { self }
fn from_le(self) -> Self { self }
fn from_be(self) -> Self { self }
fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) }
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) }
}
macro_rules! impl_endians {
( $( $t:ty ),* ) => { $(
impl EndianSensitive for $t {
fn to_le(self) -> Self { <$t>::to_le(self) }
fn to_be(self) -> Self { <$t>::to_be(self) }
fn from_le(self) -> Self { <$t>::from_le(self) }
fn from_be(self) -> Self { <$t>::from_be(self) }
fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_be(); f(&d) }
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_le(); f(&d) }
}
)* }
}
macro_rules! impl_non_endians {
( $( $t:ty ),* ) => { $(
impl EndianSensitive for $t {}
)* }
}
impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize);
impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40],
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
@@ -0,0 +1,32 @@
// 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/>.
//! Vec<u8> serialiser.
use runtime_support::Vec;
use slicable::Slicable;
/// Trait to allow itself to be serialised into a `Vec<u8>`
pub trait Joiner {
fn join<T: Slicable + Sized>(self, value: &T) -> Self;
}
impl Joiner for Vec<u8> {
fn join<T: Slicable + Sized>(mut self, value: &T) -> Vec<u8> {
value.as_slice_then(|s| self.extend_from_slice(s));
self
}
}
@@ -0,0 +1,56 @@
// 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/>.
//! Serialiser and prepender.
use runtime_support::Vec;
use slicable::Slicable;
/// Trait to allow itselg to be serialised and prepended by a given slice.
pub trait KeyedVec {
fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec<u8>;
}
macro_rules! impl_non_endians {
( $( $t:ty ),* ) => { $(
impl KeyedVec for $t {
fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec<u8> {
let mut r = prepend_key.to_vec();
r.extend_from_slice(&self[..]);
r
}
}
)* }
}
macro_rules! impl_endians {
( $( $t:ty ),* ) => { $(
impl KeyedVec for $t {
fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec<u8> {
self.as_slice_then(|slice| {
let mut r = prepend_key.to_vec();
r.extend_from_slice(slice);
r
})
}
}
)* }
}
impl_endians!(u8, i8, u16, u32, u64, usize, i16, i32, i64, isize);
impl_non_endians!([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; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
@@ -0,0 +1,23 @@
// 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/>.
//! Codec utils.
pub mod endiansensitive;
pub mod streamreader;
pub mod joiner;
pub mod slicable;
pub mod keyedvec;
@@ -0,0 +1,88 @@
// 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/>.
//! Serialisation.
use runtime_support::{Vec, size_of, transmute, uninitialized, slice};
use joiner::Joiner;
use endiansensitive::EndianSensitive;
/// Trait that allows zero-copy read/write of value-references to/from slices in LE format.
pub trait Slicable: Sized {
fn from_slice(value: &[u8]) -> Option<Self> {
Self::set_as_slice(|out| if value.len() == out.len() {
out.copy_from_slice(&value);
true
} else {
false
})
}
fn to_vec(&self) -> Vec<u8> {
self.as_slice_then(|s| s.to_vec())
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(set_slice: F) -> Option<Self>;
fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&self.to_vec())
}
fn size_of(_value: &[u8]) -> Option<usize>;
}
/// Trait to mark that a type is not trivially (essentially "in place") serialisable.
pub trait NonTrivialSlicable: Slicable {}
impl<T: EndianSensitive> Slicable for T {
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(fill_slice: F) -> Option<Self> {
let size = size_of::<T>();
let mut result: T = unsafe { uninitialized() };
let result_slice = unsafe {
slice::from_raw_parts_mut(transmute::<*mut T, *mut u8>(&mut result), size)
};
if fill_slice(result_slice) {
Some(result.from_le())
} else {
None
}
}
fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
let size = size_of::<Self>();
self.as_le_then(|le| {
let value_slice = unsafe {
slice::from_raw_parts(transmute::<*const Self, *const u8>(le), size)
};
f(value_slice)
})
}
fn size_of(_value: &[u8]) -> Option<usize> {
Some(size_of::<Self>())
}
}
impl Slicable for Vec<u8> {
fn from_slice(value: &[u8]) -> Option<Self> {
Some(value[4..].to_vec())
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
let mut r: Vec<u8> = Vec::new().join(&(self.len() as u32));
r.extend_from_slice(&self);
r
}
fn size_of(data: &[u8]) -> Option<usize> {
u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize)
}
}
@@ -0,0 +1,74 @@
// 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/>.
//! Deserialiser.
use slicable::Slicable;
/// Simple deserialiser.
pub struct StreamReader<'a> {
data: &'a[u8],
offset: usize,
}
impl<'a> StreamReader<'a> {
/// Create a new deserialiser based on the `data`.
pub fn new(data: &'a[u8]) -> Self {
StreamReader {
data: data,
offset: 0,
}
}
/// Deserialise a single item from the data stream.
pub fn read<T: Slicable>(&mut self) -> Option<T> {
let size = T::size_of(&self.data[self.offset..])?;
let new_offset = self.offset + size;
let slice = &self.data[self.offset..new_offset];
self.offset = new_offset;
Slicable::from_slice(slice)
}
}
/*
// Not in use yet
// TODO: introduce fn size_will_be(&self) -> usize; to Slicable trait and implement
struct StreamWriter<'a> {
data: &'a mut[u8],
offset: usize,
}
impl<'a> StreamWriter<'a> {
pub fn new(data: &'a mut[u8]) -> Self {
StreamWriter {
data: data,
offset: 0,
}
}
pub fn write<T: Slicable>(&mut self, value: &T) -> bool {
value.as_slice_then(|s| {
let new_offset = self.offset + s.len();
if self.data.len() <= new_offset {
let slice = &mut self.data[self.offset..new_offset];
self.offset = new_offset;
slice.copy_from_slice(s);
true
} else {
false
}
})
}
}
*/
@@ -0,0 +1,54 @@
// 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/>.
//! The Polkadot runtime. This can be compiled with #[no_std], ready for Wasm.
#![cfg_attr(feature = "without-std", no_std)]
#![cfg_attr(feature = "strict", deny(warnings))]
#[macro_use]
extern crate runtime_support;
#[cfg(test)]
extern crate rustc_hex;
mod codec;
#[macro_use]
mod support;
mod runtime;
pub use codec::{endiansensitive, streamreader, joiner, slicable, keyedvec};
pub use support::{primitives, function, environment, storable};
#[cfg(test)]
pub use support::{testing, statichex};
use runtime_support::Vec;
use slicable::Slicable;
use primitives::{Block, UncheckedTransaction};
/// Execute a block, with `input` being the canonical serialisation of the block. Returns the
/// empty vector.
pub fn execute_block(input: Vec<u8>) -> Vec<u8> {
runtime::system::execute_block(Block::from_slice(&input).unwrap());
Vec::new()
}
/// Execute a given, serialised, transaction. Returns the empty vector.
pub fn execute_transaction(input: Vec<u8>) -> Vec<u8> {
runtime::system::execute_transaction(&UncheckedTransaction::from_slice(&input).unwrap());
Vec::new()
}
impl_stubs!(execute_block, execute_transaction);
@@ -0,0 +1,44 @@
// 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/>.
//! Conensus module for runtime; manages the authority set ready for the native code.
use runtime_support::Vec;
use storable::StorageVec;
use primitives::SessionKey;
struct AuthorityStorageVec {}
impl StorageVec for AuthorityStorageVec {
type Item = SessionKey;
const PREFIX: &'static[u8] = b"con:aut:";
}
/// Get the current set of authorities. These are the session keys.
pub fn authorities() -> Vec<SessionKey> {
AuthorityStorageVec::items()
}
/// Set the current set of authorities' session keys.
///
/// Called by `next_session` only.
pub fn set_authorities(authorities: &[SessionKey]) {
AuthorityStorageVec::set_items(authorities);
}
/// Set a single authority by index.
pub fn set_authority(index: u32, key: &SessionKey) {
AuthorityStorageVec::set_item(index, key);
}
@@ -0,0 +1,30 @@
// 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/>.
//! The Polkadot runtime.
#[allow(unused)]
pub mod system;
#[allow(unused)]
pub mod consensus;
#[allow(unused)]
pub mod staking;
#[allow(unused)]
pub mod timestamp;
#[allow(unused)]
pub mod session;
// TODO: governance, polkadao
@@ -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/>.
//! Session manager: is told the validators and allows them to manage their session keys for the
//! consensus module.
use runtime_support::Vec;
use keyedvec::KeyedVec;
use storable::{kill, Storable, StorageVec};
use primitives::{AccountID, SessionKey, BlockNumber};
use runtime::{system, staking, consensus};
struct ValidatorStorageVec {}
impl StorageVec for ValidatorStorageVec {
type Item = AccountID;
const PREFIX: &'static[u8] = b"ses:val:";
}
// TRANSACTION API (available to all transactors)
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
/// session.
pub fn set_key(validator: &AccountID, key: &SessionKey) {
// set new value for next session
key.store(&validator.to_keyed_vec(b"ses:nxt:"));
}
// PUBLIC API (available to other runtime modules)
/// Get the current set of authorities. These are the session keys.
pub fn validators() -> Vec<AccountID> {
ValidatorStorageVec::items()
}
/// Set the current set of validators.
///
/// Called by staking::next_era() only. `next_session` should be called after this in order to
/// update the session keys to the next validator set.
pub fn set_validators(new: &[AccountID]) {
ValidatorStorageVec::set_items(new);
consensus::set_authorities(new);
}
/// The number of blocks in each session.
pub fn length() -> BlockNumber {
Storable::lookup_default(b"ses:len")
}
/// The current era index.
pub fn current_index() -> BlockNumber {
Storable::lookup_default(b"ses:ind")
}
/// Set the current era index.
pub fn set_current_index(new: BlockNumber) {
new.store(b"ses:ind");
}
/// The block number at which the era length last changed.
pub fn last_length_change() -> BlockNumber {
Storable::lookup_default(b"ses:llc")
}
/// Set a new era length. Won't kick in until the next era change (at current length).
pub fn set_length(new: BlockNumber) {
new.store(b"ses:nln");
}
/// 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();
}
}
// PRIVATE (not available for use externally)
/// Move onto next session: register the new authority set.
fn rotate_session() {
// Increment current session index.
set_current_index(current_index() + 1);
// Enact era length change.
if let Some(next_len) = u64::lookup(b"ses:nln") {
next_len.store(b"ses:len");
system::block_number().store(b"ses:llc");
kill(b"ses:nln");
}
// Update any changes in session keys.
validators().iter().enumerate().for_each(|(i, v)| {
let k = v.to_keyed_vec(b"ses:nxt:");
if let Some(n) = Storable::lookup(&k) {
consensus::set_authority(i as u32, &n);
kill(&k);
}
});
}
#[cfg(test)]
mod tests {
use runtime_support::{with_externalities, twox_128};
use keyedvec::KeyedVec;
use joiner::Joiner;
use testing::{one, two, TestExternalities};
use primitives::AccountID;
use runtime::{consensus, session};
use environment::with_env;
fn simple_setup() -> TestExternalities {
TestExternalities { storage: map![
twox_128(b"ses:len").to_vec() => vec![].join(&2u64),
// the validators (10, 20, ...)
twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32),
twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32],
twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32],
// initial session keys (11, 21, ...)
twox_128(b"con:aut:len").to_vec() => vec![].join(&2u32),
twox_128(&0u32.to_keyed_vec(b"con:aut:")).to_vec() => vec![11; 32],
twox_128(&1u32.to_keyed_vec(b"con:aut:")).to_vec() => vec![21; 32]
], }
}
#[test]
fn simple_setup_should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
assert_eq!(session::length(), 2u64);
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
});
}
#[test]
fn session_length_change_should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
// Block 1: Change to length 3; no visible change.
with_env(|e| e.block_number = 1);
session::set_length(3);
session::check_rotate_session();
assert_eq!(session::length(), 2);
assert_eq!(session::current_index(), 0);
// Block 2: Length now changed to 3. Index incremented.
with_env(|e| e.block_number = 2);
session::set_length(3);
session::check_rotate_session();
assert_eq!(session::length(), 3);
assert_eq!(session::current_index(), 1);
// Block 3: Length now changed to 3. Index incremented.
with_env(|e| e.block_number = 3);
session::check_rotate_session();
assert_eq!(session::length(), 3);
assert_eq!(session::current_index(), 1);
// Block 4: Change to length 2; no visible change.
with_env(|e| e.block_number = 4);
session::set_length(2);
session::check_rotate_session();
assert_eq!(session::length(), 3);
assert_eq!(session::current_index(), 1);
// Block 5: Length now changed to 2. Index incremented.
with_env(|e| e.block_number = 5);
session::check_rotate_session();
assert_eq!(session::length(), 2);
assert_eq!(session::current_index(), 2);
// Block 6: No change.
with_env(|e| e.block_number = 6);
session::check_rotate_session();
assert_eq!(session::length(), 2);
assert_eq!(session::current_index(), 2);
// Block 7: Next index.
with_env(|e| e.block_number = 7);
session::check_rotate_session();
assert_eq!(session::length(), 2);
assert_eq!(session::current_index(), 3);
});
}
#[test]
fn session_change_should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
// Block 1: No change
with_env(|e| e.block_number = 1);
session::check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 2: Session rollover, but no change.
with_env(|e| e.block_number = 2);
session::check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 3: Set new key for validator 2; no visible change.
with_env(|e| e.block_number = 3);
session::set_key(&[20; 32], &[22; 32]);
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
session::check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 4: Session rollover, authority 2 changes.
with_env(|e| e.block_number = 4);
session::check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]);
});
}
}
@@ -0,0 +1,363 @@
// 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/>.
//! Staking manager: Handles balances and periodically determines the best set of validators.
use runtime_support::Vec;
use keyedvec::KeyedVec;
use storable::{Storable, StorageVec};
use primitives::{BlockNumber, AccountID};
use runtime::{system, session};
/// The balance of an account.
pub type Balance = u64;
/// The amount of bonding period left in an account. Measured in eras.
pub type Bondage = u64;
struct IntentionStorageVec {}
impl StorageVec for IntentionStorageVec {
type Item = AccountID;
const PREFIX: &'static[u8] = b"ses:wil:";
}
// Each identity's stake may be in one of three bondage states, given by an integer:
// - n | n <= current_era(): inactive: free to be transferred.
// - ~0: active: currently representing a validator.
// - n | n > current_era(): deactivating: recently representing a validator and not yet
// ready for transfer.
/// The length of the bonding duration in eras.
pub fn bonding_duration() -> BlockNumber {
Storable::lookup_default(b"sta:loc")
}
/// The length of a staking era in sessions.
pub fn validator_count() -> usize {
u32::lookup_default(b"sta:vac") as usize
}
/// The length of a staking era in blocks.
pub fn era_length() -> BlockNumber {
sessions_per_era() * session::length()
}
/// The length of a staking era in sessions.
pub fn sessions_per_era() -> BlockNumber {
Storable::lookup_default(b"sta:spe")
}
/// The current era index.
pub fn current_era() -> BlockNumber {
Storable::lookup_default(b"sta:era")
}
/// The block number at which the era length last changed.
pub fn last_era_length_change() -> BlockNumber {
Storable::lookup_default(b"sta:lec")
}
/// The balance of a given account.
pub fn balance(who: &AccountID) -> Balance {
Storable::lookup_default(&who.to_keyed_vec(b"sta:bal:"))
}
/// The liquidity-state of a given account.
pub fn bondage(who: &AccountID) -> Bondage {
Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:"))
}
/// Transfer some unlocked staking balance to another staker.
pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) {
let from_key = transactor.to_keyed_vec(b"sta:bal:");
let from_balance = Balance::lookup_default(&from_key);
assert!(from_balance >= value);
let to_key = dest.to_keyed_vec(b"sta:bal:");
let to_balance: Balance = Storable::lookup_default(&to_key);
assert!(bondage(transactor) <= bondage(dest));
assert!(to_balance + value > to_balance); // no overflow
(from_balance - value).store(&from_key);
(to_balance + value).store(&to_key);
}
/// Declare the desire to stake for the transactor.
///
/// Effects will be felt at the beginning of the next era.
pub fn stake(transactor: &AccountID) {
let mut intentions = IntentionStorageVec::items();
// can't be in the list twice.
assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked.");
intentions.push(transactor.clone());
IntentionStorageVec::set_items(&intentions);
u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:"));
}
/// 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);
(current_era() + bonding_duration()).store(&transactor.to_keyed_vec(b"sta:bon:"));
}
/// Hook to be called after to transaction processing.
pub fn check_new_era() {
// check block number and call new_era if necessary.
if (system::block_number() - last_era_length_change()) % era_length() == 0 {
new_era();
}
}
// PRIVATE
/// The era has changed - enact new staking set.
///
/// NOTE: This always happens on a session change.
fn new_era() {
// Increment current era.
(current_era() + 1).store(b"sta:era");
// Enact era length change.
let next_spe: u64 = Storable::lookup_default(b"sta:nse");
if next_spe > 0 && next_spe != sessions_per_era() {
next_spe.store(b"sta:spe");
system::block_number().store(b"sta:lec");
}
// TODO: evaluate desired staking amounts and nominations and optimise to find the best
// combination of validators, then use session::set_validators().
// for now, this just orders would-be stakers by their balances and chooses the top-most
// validator_count() of them.
let mut intentions = IntentionStorageVec::items()
.into_iter()
.map(|v| (balance(&v), v))
.collect::<Vec<_>>();
intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1));
session::set_validators(
&intentions.into_iter()
.map(|(_, v)| v)
.take(validator_count())
.collect::<Vec<_>>()
);
}
/// Set a new era length. Won't kick in until the next era change (at current length).
fn set_sessions_per_era(new: BlockNumber) {
new.store(b"sta:nse");
}
#[cfg(test)]
mod tests {
use runtime_support::{with_externalities, twox_128};
use keyedvec::KeyedVec;
use joiner::Joiner;
use testing::{one, two, TestExternalities};
use primitives::AccountID;
use runtime::{staking, session};
use environment::with_env;
#[test]
fn staking_should_work() {
let one = one();
let two = two();
let three = [3u8; 32];
let four = [4u8; 32];
let mut t = TestExternalities { storage: map![
twox_128(b"ses:len").to_vec() => vec![].join(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32),
twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32],
twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32],
twox_128(b"sta:spe").to_vec() => vec![].join(&2u64),
twox_128(b"sta:vac").to_vec() => vec![].join(&2u32),
twox_128(b"sta:loc").to_vec() => vec![].join(&3u64),
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&10u64),
twox_128(&two.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&20u64),
twox_128(&three.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&30u64),
twox_128(&four.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&40u64)
], };
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 2u64);
assert_eq!(staking::validator_count(), 2usize);
assert_eq!(staking::bonding_duration(), 3u64);
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
// Block 1: Add three validators. No obvious change.
with_env(|e| e.block_number = 1);
staking::stake(&one);
staking::stake(&two);
staking::stake(&four);
staking::check_new_era();
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
// Block 2: New validator set now.
with_env(|e| e.block_number = 2);
staking::check_new_era();
assert_eq!(session::validators(), vec![four.clone(), two.clone()]);
// Block 3: Unstake highest, introduce another staker. No change yet.
with_env(|e| e.block_number = 3);
staking::stake(&three);
staking::unstake(&four);
staking::check_new_era();
// Block 4: New era - validators change.
with_env(|e| e.block_number = 4);
staking::check_new_era();
assert_eq!(session::validators(), vec![three.clone(), two.clone()]);
// Block 5: Transfer stake from highest to lowest. No change yet.
with_env(|e| e.block_number = 5);
staking::transfer(&four, &one, 40);
staking::check_new_era();
// Block 6: Lowest now validator.
with_env(|e| e.block_number = 6);
staking::check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 7: Unstake three. No change yet.
with_env(|e| e.block_number = 7);
staking::unstake(&three);
staking::check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 8: Back to one and two.
with_env(|e| e.block_number = 8);
staking::check_new_era();
assert_eq!(session::validators(), vec![one.clone(), two.clone()]);
});
}
#[test]
fn staking_eras_work() {
let mut t = TestExternalities { storage: map![
twox_128(b"ses:len").to_vec() => vec![].join(&1u64),
twox_128(b"sta:spe").to_vec() => vec![].join(&2u64)
], };
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 2u64);
assert_eq!(staking::sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 0u64);
// Block 1: No change.
with_env(|e| e.block_number = 1);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 0u64);
// Block 2: Simple era change.
with_env(|e| e.block_number = 2);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 1u64);
// Block 3: Schedule an era length change; no visible changes.
with_env(|e| e.block_number = 3);
staking::set_sessions_per_era(3);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 2u64);
assert_eq!(staking::last_era_length_change(), 0u64);
assert_eq!(staking::current_era(), 1u64);
// Block 4: Era change kicks in.
with_env(|e| e.block_number = 4);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 2u64);
// Block 5: No change.
with_env(|e| e.block_number = 5);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 2u64);
// Block 6: No change.
with_env(|e| e.block_number = 6);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 2u64);
// Block 7: Era increment.
with_env(|e| e.block_number = 7);
staking::check_new_era();
assert_eq!(staking::sessions_per_era(), 3u64);
assert_eq!(staking::last_era_length_change(), 4u64);
assert_eq!(staking::current_era(), 3u64);
});
}
#[test]
fn staking_balance_works() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&42u64)
], };
with_externalities(&mut t, || {
assert_eq!(staking::balance(&one), 42);
assert_eq!(staking::balance(&two), 0);
});
}
#[test]
fn staking_balance_transfer_works() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&111u64)
], };
with_externalities(&mut t, || {
staking::transfer(&one, &two, 69);
assert_eq!(staking::balance(&one), 42);
assert_eq!(staking::balance(&two), 69);
});
}
#[test]
#[should_panic]
fn staking_balance_transfer_when_bonded_doesnt_work() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![].join(&111u64)
], };
with_externalities(&mut t, || {
staking::stake(&one);
staking::transfer(&one, &two, 69);
});
}
}
@@ -0,0 +1,154 @@
// 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/>.
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
//! and depositing logs.
use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder, Hashable};
use runtime_support::{Vec, swap};
use storable::Storable;
use keyedvec::KeyedVec;
use environment::with_env;
use runtime::{staking, session};
/// The current block number being processed. Set by `execute_block`.
pub fn block_number() -> BlockNumber {
with_env(|e| e.block_number)
}
/// Get the block hash of a given block (uses storage).
pub fn block_hash(number: BlockNumber) -> Hash {
Storable::lookup_default(&number.to_keyed_vec(b"sys:old:"))
}
/// Deposits a log and ensures it matches the blocks log data.
pub fn deposit_log(log: &[u8]) {
with_env(|e| {
assert_eq!(log, &e.digest.logs[e.next_log_index][..]);
e.next_log_index += 1;
});
}
pub fn execute_block(mut block: Block) {
// populate environment from header.
with_env(|e| {
e.block_number = block.header.number;
swap(&mut e.digest, &mut block.header.digest);
e.next_log_index = 0;
});
let ref header = block.header;
// check parent_hash is correct.
assert!(
header.number > 0 && block_hash(header.number - 1) == header.parent_hash,
"Parent hash should be valid."
);
// TODO: check transaction trie root represents the transactions.
// this requires non-trivial changes to the externals API or compiling trie rooting into wasm
// so will wait until a little later.
// store the header hash in storage.
let header_hash_key = header.number.to_keyed_vec(b"sys:old:");
header.blake2_256().store(&header_hash_key);
// execute transactions
block.transactions.iter().for_each(execute_transaction);
staking::check_new_era();
session::check_rotate_session();
// any final checks
final_checks(&block);
// TODO: check storage root.
// this requires non-trivial changes to the externals API or compiling trie rooting into wasm
// so will wait until a little later.
}
/// Execute a given transaction.
pub fn execute_transaction(utx: &UncheckedTransaction) {
// Verify the signature is good.
assert!(utx.ed25519_verify(), "All transactions should be properly signed");
let ref tx = utx.transaction;
// check nonce
let nonce_key = tx.signed.to_keyed_vec(b"sys:non:");
let expected_nonce: TxOrder = Storable::lookup_default(&nonce_key);
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce");
// increment nonce in storage
(expected_nonce + 1).store(&nonce_key);
// decode parameters and dispatch
tx.function.dispatch(&tx.signed, &tx.input_data);
}
/// Set the new code.
pub fn set_code(new: &[u8]) {
new.store(b":code");
}
fn final_checks(_block: &Block) {
with_env(|e| {
assert_eq!(e.next_log_index, e.digest.logs.len());
});
}
#[cfg(test)]
mod tests {
use joiner::Joiner;
use function::Function;
use keyedvec::KeyedVec;
use slicable::Slicable;
use runtime_support::{with_externalities, twox_128};
use primitives::{UncheckedTransaction, Transaction};
use statichex::StaticHexInto;
use runtime::{system, staking};
use testing::{TestExternalities, HexDisplay, one, two};
#[test]
fn staking_balance_transfer_dispatch_works() {
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 tx = UncheckedTransaction {
transaction: Transaction {
signed: one.clone(),
nonce: 0,
function: Function::StakingTransfer,
input_data: vec![].join(&two).join(&69u64),
},
signature: "679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a".convert(),
};
// tx: 2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000228000000d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000
// sig: 679fcf0a846b4224c84ecad7d91a26241c46d00cb53d6480a363274e8965ee34b0b80b4b2e3836d3d8f8f12c0c1aef7350af587d9aee3883561d11726068ac0a
println!("tx is {}", HexDisplay::from(&tx.transaction.to_vec()));
with_externalities(&mut t, || {
system::execute_transaction(&tx);
assert_eq!(staking::balance(&one), 42);
assert_eq!(staking::balance(&two), 69);
});
}
}
@@ -0,0 +1,54 @@
// 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/>.
//! Timestamp manager: just handles the current timestamp.
use storable::Storable;
/// Representation of a time.
pub type Timestamp = u64;
/// Get the current time.
pub fn get() -> Timestamp {
Storable::lookup_default(b"tim:val")
}
/// Set the current time.
pub fn set(now: Timestamp) {
now.store(b"tim:val")
}
#[cfg(test)]
mod tests {
use joiner::Joiner;
use keyedvec::KeyedVec;
use runtime_support::{with_externalities, twox_128};
use runtime::timestamp;
use testing::TestExternalities;
#[test]
fn timestamp_works() {
let mut t = TestExternalities { storage: map![
twox_128(b"tim:val").to_vec() => vec![].join(&42u64)
], };
with_externalities(&mut t, || {
assert_eq!(timestamp::get(), 42);
timestamp::set(69);
assert_eq!(timestamp::get(), 69);
});
}
}
@@ -0,0 +1,78 @@
// 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/>.
//! Environment API: Allows certain information to be accessed throughout the runtime.
use runtime_support::{Rc, RefCell, transmute, Box};
use primitives::{BlockNumber, Digest};
#[derive(Default)]
/// The information that can be accessed globally.
pub struct Environment {
/// The current block number.
pub block_number: BlockNumber,
/// The current block digest.
pub digest: Digest,
/// The number of log items in this block that have been accounted for so far.
pub next_log_index: usize,
}
/// Do something with the environment and return its value. Keep the function short.
pub fn with_env<T, F: FnOnce(&mut Environment) -> T>(f: F) -> T {
let e = env();
let mut eb = e.borrow_mut();
f(&mut *eb)
}
#[cfg(not(test))]
fn env() -> Rc<RefCell<Environment>> {
// Initialize it to a null value
static mut SINGLETON: *const Rc<RefCell<Environment>> = 0 as *const Rc<RefCell<Environment>>;
unsafe {
if SINGLETON == 0 as *const Rc<RefCell<Environment>> {
// Make it
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
// Put it in the heap so it can outlive this call
SINGLETON = transmute(Box::new(singleton));
}
// Now we give out a copy of the data that is safe to use concurrently.
(*SINGLETON).clone()
}
}
#[cfg(test)]
fn env() -> Rc<RefCell<Environment>> {
// Initialize it to a null value
thread_local!{
static SINGLETON: RefCell<*const Rc<RefCell<Environment>>> = RefCell::new(0 as *const Rc<RefCell<Environment>>);
}
SINGLETON.with(|s| unsafe {
if *s.borrow() == 0 as *const Rc<RefCell<Environment>> {
// Make it
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
// Put it in the heap so it can outlive this call
*s.borrow_mut() = transmute(Box::new(singleton));
}
// Now we give out a copy of the data that is safe to use concurrently.
(**s.borrow()).clone()
})
}
@@ -0,0 +1,74 @@
// 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/>.
//! Function data: This describes a function that can be called from an external transaction.
use primitives::AccountID;
use streamreader::StreamReader;
use runtime::{staking, session, timestamp};
/// The functions that a transaction can call (and be dispatched to).
#[cfg_attr(test, derive(PartialEq, Debug))]
#[derive(Clone, Copy)]
pub enum Function {
StakingStake,
StakingUnstake,
StakingTransfer,
SessionSetKey,
TimestampSet,
}
impl Function {
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
pub fn from_u8(value: u8) -> Option<Function> {
match value {
x if x == Function::StakingStake as u8 => Some(Function::StakingStake),
x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake),
x if x == Function::StakingTransfer as u8 => Some(Function::StakingTransfer),
x if x == Function::SessionSetKey as u8 => Some(Function::SessionSetKey),
x if x == Function::TimestampSet as u8 => Some(Function::TimestampSet),
_ => None,
}
}
}
impl Function {
/// Dispatch the function.
pub fn dispatch(&self, transactor: &AccountID, data: &[u8]) {
let mut params = StreamReader::new(data);
match *self {
Function::StakingStake => {
staking::stake(transactor);
}
Function::StakingUnstake => {
staking::unstake(transactor);
}
Function::StakingTransfer => {
let dest = params.read().unwrap();
let value = params.read().unwrap();
staking::transfer(transactor, &dest, value);
}
Function::SessionSetKey => {
let session = params.read().unwrap();
session::set_key(transactor, &session);
}
Function::TimestampSet => {
let t = params.read().unwrap();
timestamp::set(t);
}
}
}
}
@@ -0,0 +1,28 @@
// 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/>.
//! Support code for the runtime.
pub mod primitives;
pub mod function;
pub mod environment;
pub mod storable;
#[cfg(test)]
pub mod statichex;
#[cfg(test)]
#[macro_use]
pub mod testing;
@@ -0,0 +1,515 @@
// 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::Vec;
use streamreader::StreamReader;
use joiner::Joiner;
use slicable::{Slicable, NonTrivialSlicable};
use function::Function;
use runtime_support::{size_of, 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 = size_of::<Hash>() + size_of::<BlockNumber>() + size_of::<Hash>() + 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 = size_of::<AccountID>() + size_of::<TxOrder>() + 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 = 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);
}
}
@@ -0,0 +1,52 @@
// 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/>.
//! Interpret a static string of hex as a desired type.
use rustc_hex::FromHex;
/// Trait to allow conversion from a static hex string to an instance.
pub trait StaticHexConversion: Sized {
/// Convert the static str into Self. Use just like `From::from`.
fn from_static_hex(hex: &'static str) -> Self;
}
macro_rules! impl_sizes {
( $( $t:expr ),* ) => { $(
impl StaticHexConversion for [u8; $t] {
fn from_static_hex(hex: &'static str) -> Self {
let mut r = [0u8; $t];
r.copy_from_slice(&FromHex::from_hex(hex).unwrap());
r
}
}
)* }
}
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);
/// Trait to allow converting from itself (only implemented for a static str) into some useful
/// type (which must implement `StaticHexConversion`).
pub trait StaticHexInto {
/// Convert self (i.e. a static str) into the appropriate type. Use just like `Into::into`.
fn convert<T: StaticHexConversion>(self) -> T;
}
impl StaticHexInto for &'static str {
fn convert<T: StaticHexConversion>(self) -> T {
T::from_static_hex(self)
}
}
@@ -0,0 +1,87 @@
// 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::{self, twox_128, Vec};
/// 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 + Sized + 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,106 @@
// 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/>.
//! Testing helpers.
use runtime_support::{NoError, Externalities};
use std::collections::HashMap;
use primitives::AccountID;
use statichex::StaticHexInto;
#[derive(Debug, Default)]
/// Simple externaties implementation.
pub struct TestExternalities {
/// The storage map.
pub storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl Externalities for TestExternalities {
type Error = NoError;
fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
}
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.storage.insert(key, value);
}
fn chain_id(&self) -> u64 { 42 }
}
#[macro_export]
macro_rules! map {
($( $name:expr => $value:expr ),*) => (
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
)
}
/// One account (to which we know the secret key).
pub fn one() -> AccountID {
"2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee".convert()
}
/// Another account (secret key known).
pub fn two() -> AccountID {
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".convert()
}
/// Hex display, this time for no_std. See main codebase for documentation.
pub struct HexDisplay<'a>(&'a [u8]);
impl<'a> HexDisplay<'a> {
/// See main codebase for documentation.
pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) }
}
impl<'a> ::std::fmt::Display for HexDisplay<'a> {
fn fmt(&self, fmtr: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
for byte in self.0 {
try!( fmtr.write_fmt(format_args!("{:02x}", byte)));
}
Ok(())
}
}
/// See main codebase for documentation.
pub trait AsBytesRef {
/// See main codebase for documentation.
fn as_bytes_ref(&self) -> &[u8];
}
impl AsBytesRef for [u8] {
fn as_bytes_ref(&self) -> &[u8] { &self }
}
impl<'a> AsBytesRef for &'a[u8] {
fn as_bytes_ref(&self) -> &[u8] { self }
}
impl AsBytesRef for Vec<u8> {
fn as_bytes_ref(&self) -> &[u8] { &self[..] }
}
macro_rules! impl_non_endians {
( $( $t:ty ),* ) => { $(
impl AsBytesRef for $t {
fn as_bytes_ref(&self) -> &[u8] { &self[..] }
}
)* }
}
impl_non_endians!([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; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
@@ -0,0 +1,18 @@
[package]
name = "pwasm-alloc"
version = "0.1.0"
authors = ["Nikolay Volf <nikvolf@gmail.com>"]
license = "MIT/Apache-2.0"
readme = "README.md"
repository = "https://github.com/paritytech/pwasm-std"
homepage = "https://github.com/paritytech/pwasm-std"
documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/"
description = "Parity WebAssembly standard library internal allocator"
keywords = ["wasm", "parity", "webassembly", "blockchain"]
categories = ["no-std", "embedded"]
[dependencies]
pwasm-libc = { path = "../pwasm-libc", version = "0.1" }
[features]
strict = []
@@ -0,0 +1,12 @@
# pwasm-libc
Parity WASM contracts standard library libc bindings
[Documentation](https://paritytech.github.io/pwasm-std/pwasm_alloc/)
# License
`pwasm_alloc` is primarily distributed under the terms of both the MIT
license and the Apache License (Version 2.0), at your choice.
See LICENSE-APACHE, and LICENSE-MIT for details.
@@ -0,0 +1,30 @@
#![warn(missing_docs)]
#![cfg_attr(feature = "strict", deny(warnings))]
#![no_std]
#![crate_type = "rlib"]
#![feature(global_allocator)]
#![feature(alloc)]
#![feature(allocator_api)]
//! Custom allocator crate for wasm
extern crate alloc;
extern crate pwasm_libc;
use alloc::heap::{Alloc, Layout, AllocErr};
/// Wasm allocator
pub struct WasmAllocator;
unsafe impl<'a> Alloc for &'a WasmAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
Ok(pwasm_libc::malloc(layout.size()))
}
unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) {
pwasm_libc::free(ptr)
}
}
#[global_allocator]
static ALLOCATOR: WasmAllocator = WasmAllocator;
@@ -0,0 +1,15 @@
[package]
name = "pwasm-libc"
version = "0.1.0"
authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"]
license = "MIT/Apache-2.0"
readme = "README.md"
repository = "https://github.com/paritytech/pwasm-std"
homepage = "https://github.com/paritytech/pwasm-std"
documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/"
description = "Parity WebAssembly standard library libc bindings"
keywords = ["wasm", "parity", "webassembly", "blockchain"]
categories = ["no-std", "embedded"]
[features]
strict = []
@@ -0,0 +1,12 @@
# pwasm-libc
Parity WASM contracts standard library libc bindings
[Documentation](https://paritytech.github.io/pwasm-std/pwasm_libc/)
# License
`pwasm-libc` is primarily distributed under the terms of both the MIT
license and the Apache License (Version 2.0), at your choice.
See LICENSE-APACHE, and LICENSE-MIT for details.
@@ -0,0 +1,46 @@
#![warn(missing_docs)]
#![cfg_attr(feature = "strict", deny(warnings))]
#![no_std]
//! libc externs crate
extern "C" {
fn ext_memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
fn ext_memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
fn ext_memset(dest: *mut u8, c: i32, n: usize) -> *mut u8;
fn ext_malloc(size: usize) -> *mut u8;
fn ext_free(ptr: *mut u8);
}
// Declaring these function here prevents Emscripten from including it's own verisons
// into final binary.
/// memcpy extern
#[no_mangle]
pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
ext_memcpy(dest, src, n)
}
/// memmove extern
#[no_mangle]
pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
ext_memmove(dest, src, n)
}
/// memset extern
#[no_mangle]
pub unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 {
ext_memset(dest, c, n)
}
/// malloc extern
#[no_mangle]
pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
ext_malloc(size)
}
/// free extern
#[no_mangle]
pub unsafe extern "C" fn free(ptr: *mut u8) {
ext_free(ptr);
}
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "runtime-support"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
pwasm-libc = { path = "../pwasm-libc", version = "0.1" }
pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" }
[features]
strict = []
+144
View File
@@ -0,0 +1,144 @@
#![no_std]
#![feature(lang_items)]
#![feature(alloc)]
#![cfg_attr(feature = "strict", deny(warnings))]
#![feature(alloc)]
//#[macro_use]
extern crate alloc;
pub use alloc::vec::Vec;
pub use alloc::boxed::Box;
pub use alloc::rc::Rc;
pub use core::mem::{transmute, size_of, uninitialized, swap};
pub use core::slice;
pub use core::cell::{RefCell, Ref, RefMut};
extern crate pwasm_libc;
extern crate pwasm_alloc;
#[lang = "panic_fmt"]
#[no_mangle]
pub fn panic_fmt() -> ! {
loop {}
}
extern "C" {
fn ext_print(utf8_data: *const u8, utf8_len: u32);
fn ext_print_num(value: u64);
fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32);
fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8;
fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32;
fn ext_chain_id() -> u64;
fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_128(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_256(data: *const u8, len: u32, out: *mut u8);
fn ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32;
}
pub fn storage(key: &[u8]) -> Vec<u8> {
let mut length: u32 = 0;
unsafe {
let ptr = ext_get_allocated_storage(&key[0], key.len() as u32, &mut length);
Vec::from_raw_parts(ptr, length as usize, length as usize)
}
}
pub fn set_storage(key: &[u8], value: &[u8]) {
unsafe {
ext_set_storage(
&key[0] as *const u8, key.len() as u32,
&value[0] as *const u8, value.len() as u32
);
}
}
pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize {
unsafe {
ext_get_storage_into(&key[0], key.len() as u32, &mut value_out[0], value_out.len() as u32) as usize
}
}
/// The current relay chain identifier.
pub fn chain_id() -> u64 {
unsafe {
ext_chain_id()
}
}
/// Conduct a 256-bit Blake2 hash.
pub fn blake2_256(data: &[u8]) -> [u8; 32] {
unsafe {
let mut result: [u8; 32] = uninitialized();
// guaranteed to write into result.
ext_blake2_256(&data[0], data.len() as u32, &mut result[0]);
result
}
}
/// Conduct four XX hashes to give a 256-bit result.
pub fn twox_256(data: &[u8]) -> [u8; 32] {
unsafe {
let mut result: [u8; 32] = uninitialized();
// guaranteed to write into result.
ext_twox_256(&data[0], data.len() as u32, &mut result[0]);
result
}
}
/// Conduct two XX hashes to give a 256-bit result.
pub fn twox_128(data: &[u8]) -> [u8; 16] {
unsafe {
let mut result: [u8; 16] = uninitialized();
// guaranteed to write into result.
ext_twox_128(&data[0], data.len() as u32, &mut result[0]);
result
}
}
/// Verify a ed25519 signature.
pub fn ed25519_verify(sig: &[u8], msg: &[u8], pubkey: &[u8]) -> bool {
sig.len() != 64 || pubkey.len() != 32 || unsafe {
ext_ed25519_verify(&msg[0], msg.len() as u32, &sig[0], &pubkey[0])
} == 0
}
pub trait Printable {
fn print(self);
}
impl<'a> Printable for &'a [u8] {
fn print(self) {
unsafe {
ext_print(&self[0] as *const u8, self.len() as u32);
}
}
}
impl Printable for u64 {
fn print(self) {
unsafe { ext_print_num(self); }
}
}
pub fn print<T: Printable + Sized>(value: T) {
value.print();
}
#[macro_export]
macro_rules! impl_stubs {
( $( $name:ident ),* ) => {
pub mod _internal {
$(
#[no_mangle]
pub fn $name(input_data: *mut u8, input_len: usize) -> u64 {
let input = unsafe {
$crate::Vec::from_raw_parts(input_data, input_len, input_len)
};
let output = super::$name(input);
&output[0] as *const u8 as u64 + ((output.len() as u64) << 32)
}
)*
}
}
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "runtime-test"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
runtime-support = { path = "../support", version = "0.1" }
+46
View File
@@ -0,0 +1,46 @@
#![no_std]
#![feature(lang_items)]
#![cfg_attr(feature = "strict", deny(warnings))]
#![feature(alloc)]
extern crate alloc;
use alloc::vec::Vec;
#[macro_use]
extern crate runtime_support;
use runtime_support::{set_storage, storage, print, blake2_256, twox_128, twox_256, ed25519_verify};
fn test_blake2_256(input: Vec<u8>) -> Vec<u8> {
blake2_256(&input).to_vec()
}
fn test_twox_256(input: Vec<u8>) -> Vec<u8> {
twox_256(&input).to_vec()
}
fn test_twox_128(input: Vec<u8>) -> Vec<u8> {
twox_128(&input).to_vec()
}
fn test_ed25519_verify(input: Vec<u8>) -> Vec<u8> {
let sig = &input[0..64];
let pubkey = &input[64..96];
let msg = b"all ok!";
[ed25519_verify(sig, &msg[..], pubkey) as u8].to_vec()
}
fn test_data_in(input: Vec<u8>) -> Vec<u8> {
print(b"set_storage" as &[u8]);
set_storage(b"input", &input);
print(b"storage" as &[u8]);
let foo = storage(b"foo");
print(b"set_storage" as &[u8]);
set_storage(b"baz", &foo);
print(b"finished!" as &[u8]);
b"all ok!".to_vec()
}
impl_stubs!(test_data_in, test_blake2_256, test_twox_256, test_twox_128, test_ed25519_verify);