fully refactor runtime-std to use conditional compilation

This commit is contained in:
Robert Habermeier
2018-01-30 23:47:29 +01:00
parent 2c39e247d6
commit 4b2bd5ec72
22 changed files with 946 additions and 124 deletions
+17 -2
View File
@@ -1,6 +1,21 @@
[package]
name = "runtime-std"
name = "polkadot-runtime-std"
version = "0.1.0"
authors = ["Robert Habermeier <rphmeier@gmail.com>"]
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
[build-dependencies]
rustc_version = "0.2"
[dependencies]
pwasm-alloc = { path = "../wasm-runtime/pwasm-alloc", version = "0.1" }
pwasm-libc = { path = "../wasm-runtime/pwasm-libc", version = "0.1" }
environmental = { path = "../environmental", version = "0.1", optional = true }
polkadot-state-machine = { path = "../state-machine", version = "0.1", optional = true }
polkadot-primitives = { path = "../primitives", version = "0.1", optional = true }
[features]
default = ["std"]
std = ["environmental", "polkadot-state-machine", "polkadot-primitives"]
nightly = []
strict = []
+14
View File
@@ -0,0 +1,14 @@
//! Set a nightly feature
extern crate rustc_version;
use rustc_version::{version, version_meta, Channel};
fn main() {
// Assert we haven't travelled back in time
assert!(version().unwrap().major >= 1);
// Set cfg flags depending on release channel
if let Channel::Nightly = version_meta().unwrap().channel {
println!("cargo:rustc-cfg=feature=\"nightly\"");
}
}
+38 -6
View File
@@ -1,7 +1,39 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
// 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/>.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(lang_items))]
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
#![cfg_attr(not(feature = "std"), feature(alloc))]
#![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")]
#![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")]
#[cfg(feature = "std")]
include!("../with_std.rs");
#[cfg(not(feature = "std"))]
include!("../without_std.rs");
/// Prelude of common useful imports.
///
/// This should include only things which are in the normal std prelude.
pub mod prelude {
pub use ::vec::Vec;
pub use ::boxed::Box;
}
+197
View File
@@ -0,0 +1,197 @@
// 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/>.
#[macro_use]
extern crate environmental;
extern crate polkadot_state_machine;
extern crate polkadot_primitives as primitives;
use std::fmt;
use primitives::ed25519;
pub use std::vec;
pub use std::rc;
pub use std::cell;
pub use std::boxed;
pub use std::slice;
pub use std::mem;
pub use polkadot_state_machine::{Externalities, ExternalitiesError};
use primitives::hexdisplay::HexDisplay;
// TODO: use the real error, not NoError.
#[derive(Debug)]
/// As it says - an empty type we use for errors.
pub struct NoError;
impl fmt::Display for NoError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") }
}
environmental!(ext : trait Externalities);
/// Get `key` from storage and return a `Vec`, empty if there's a problem.
pub fn storage(key: &[u8]) -> Vec<u8> {
ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec()))
.expect("read_storage cannot be called outside of an Externalities-provided environment.")
.unwrap_or_else(Vec::new)
}
/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return
/// the number of bytes that the key in storage was.
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize {
ext::with(|ext| {
if let Ok(value) = ext.storage(key) {
let value = &value[value_offset..];
let written = ::std::cmp::min(value.len(), value_out.len());
value_out[0..written].copy_from_slice(&value[0..written]);
value.len()
} else {
// no-entry is treated as an empty vector of bytes.
// TODO: consider allowing empty-vector to exist separately to no-entry (i.e. return
// Option<usize>)
0
}
}).expect("read_storage cannot be called outside of an Externalities-provided environment.")
}
/// Set the storage to some particular key.
pub fn set_storage(key: &[u8], value: &[u8]) {
ext::with(|ext|
ext.set_storage(key.to_vec(), value.to_vec())
);
}
/// The current relay chain identifier.
pub fn chain_id() -> u64 {
ext::with(|ext|
ext.chain_id()
).unwrap_or(0)
}
/// Conduct a Keccak-256 hash of the given data.
pub use primitives::{blake2_256, twox_128, twox_256};
/// Verify a ed25519 signature.
pub fn ed25519_verify(sig: &[u8; 64], msg: &[u8], pubkey: &[u8; 32]) -> bool {
ed25519::verify(&sig[..], msg, &pubkey[..])
}
/// Execute the given closure with global function available whose functionality routes into the
/// externalities `ext`. Forwards the value that the closure returns.
pub fn with_externalities<R, F: FnOnce() -> R>(ext: &mut Externalities, f: F) -> R {
ext::using(ext, f)
}
/// Trait for things which can be printed.
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);
}
}
/// Print a printable value.
pub fn print<T: Printable + Sized>(value: T) {
value.print();
}
#[macro_export]
macro_rules! impl_stubs {
($( $name:ident ),*) => {}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[derive(Debug, Default)]
struct TestExternalities {
storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl Externalities for TestExternalities {
fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
}
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.storage.insert(key, value);
}
fn chain_id(&self) -> u64 { 42 }
}
macro_rules! map {
($( $name:expr => $value:expr ),*) => (
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
)
}
#[test]
fn storage_works() {
let mut t = TestExternalities { storage: map![], };
assert!(with_externalities(&mut t, || {
assert_eq!(storage(b"hello"), b"".to_vec());
set_storage(b"hello", b"world");
assert_eq!(storage(b"hello"), b"world".to_vec());
assert_eq!(storage(b"foo"), b"".to_vec());
set_storage(b"foo", &[1, 2, 3][..]);
true
}));
t.storage = map![b"foo".to_vec() => b"bar".to_vec()];
assert!(!with_externalities(&mut t, || {
assert_eq!(storage(b"hello"), b"".to_vec());
assert_eq!(storage(b"foo"), b"bar".to_vec());
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");
});
}
}
+167
View File
@@ -0,0 +1,167 @@
#[cfg(feature = "nightly")]
extern crate alloc;
#[cfg(feature = "nightly")]
extern crate pwasm_libc;
#[cfg(feature = "nightly")]
extern crate pwasm_alloc;
pub use alloc::vec;
pub use alloc::boxed;
pub use alloc::rc;
pub use core::mem;
pub use core::slice;
pub use core::cell;
use alloc::vec::Vec;
#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn panic_fmt(_fmt: ::core::fmt::Arguments, _file: &'static str, _line: u32, _col: u32) {
unsafe {
ext_print_utf8(_file.as_ptr() as *const u8, _file.len() as u32);
ext_print_num(_line as u64);
ext_print_num(_col as u64);
::core::intrinsics::abort()
}
}
extern "C" {
fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32);
fn ext_print_hex(data: *const u8, len: u32);
fn ext_print_num(value: u64);
fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32);
fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8;
fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32;
fn ext_chain_id() -> u64;
fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8);
fn ext_twox_128(data: *const u8, len: u32, out: *mut u8);
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;
}
/// Get `key` from storage and return a `Vec`, empty if there's a problem.
pub fn storage(key: &[u8]) -> Vec<u8> {
let mut length: u32 = 0;
unsafe {
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)
}
}
/// Set the storage to some particular key.
pub fn set_storage(key: &[u8], value: &[u8]) {
unsafe {
ext_set_storage(
key.as_ptr(), key.len() as u32,
value.as_ptr(), value.len() as u32
);
}
}
/// Get `key` from storage, placing the value into `value_out` (as much as possible) and return
/// the number of bytes that the key in storage was.
pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize {
unsafe {
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
}
}
/// 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] {
let mut result: [u8; 32] = Default::default();
// guaranteed to write into result.
unsafe {
ext_blake2_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
}
result
}
/// Conduct four XX hashes to give a 256-bit result.
pub fn twox_256(data: &[u8]) -> [u8; 32] {
let mut result: [u8; 32] = Default::default();
// guaranteed to write into result.
unsafe {
ext_twox_256(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
}
result
}
/// Conduct two XX hashes to give a 128-bit result.
pub fn twox_128(data: &[u8]) -> [u8; 16] {
let mut result: [u8; 16] = Default::default();
// guaranteed to write into result.
unsafe {
ext_twox_128(data.as_ptr(), data.len() as u32, result.as_mut_ptr());
}
result
}
/// Verify a ed25519 signature.
pub fn ed25519_verify(sig: &[u8], msg: &[u8], pubkey: &[u8]) -> bool {
sig.len() == 64 && pubkey.len() == 32 && unsafe {
ext_ed25519_verify(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ptr())
} == 0
}
/// Trait for things which can be printed.
pub trait Printable {
fn print(self);
}
impl<'a> Printable for &'a [u8] {
fn print(self) {
unsafe {
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);
}
}
}
impl Printable for u64 {
fn print(self) {
unsafe { ext_print_num(self); }
}
}
/// Print a printable value.
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 = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
$crate::slice::from_raw_parts(input_data, input_len)
}
};
let output = super::$name(input);
output.as_ptr() as u64 + ((output.len() as u64) << 32)
}
)*
}
}
}