Introduce toy runtime for testing inside substrate. (#66)

* Introduce simple blockchain runtime for substrate tests.

* Remove bad files.

* Add needed wasm binaries.

* Refactoring.

- Repot files in test-runtime.
- Rename troublesome `Joiner::join` to `Joiner::and`.
- Rework `Slicable` to dedup code.

* More fixes and refactoring

* Rebuild substrate test wasm.

* Fix merge errors.

* Rename the disasterously named `to_vec` to `encode`.

Also rename `as_slice_then` to `with_encoded`.

* Tests for toy runtime.

* Fix doc nit
This commit is contained in:
Gav Wood
2018-02-09 13:24:30 +01:00
committed by GitHub
parent a00d0e75fd
commit dec6e82387
65 changed files with 1923 additions and 496 deletions
+8 -6
View File
@@ -7,11 +7,13 @@ authors = ["Parity Technologies <admin@parity.io>"]
error-chain = "0.11"
log = "0.3"
parking_lot = "0.4"
substrate-primitives = { path = "../primitives", version = "0.1" }
substrate-state-machine = { path = "../state-machine", version = "0.1" }
substrate-serializer = { path = "../serializer" }
substrate-executor = { path = "../executor" }
substrate-codec = { path = "../codec", version = "0.1" }
triehash = "0.1"
hex-literal = "0.1"
ed25519 = { path = "../ed25519", version = "0.1" }
ed25519 = { path = "../ed25519" }
substrate-codec = { path = "../codec" }
substrate-executor = { path = "../executor" }
substrate-primitives = { path = "../primitives" }
substrate-runtime-support = { path = "../runtime-support" }
substrate-serializer = { path = "../serializer" }
substrate-state-machine = { path = "../state-machine" }
substrate-test-runtime = { path = "../test-runtime" }
+208
View File
@@ -0,0 +1,208 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Tool for creating the genesis block.
use std::collections::HashMap;
use primitives::{Block, Header};
use triehash::trie_root;
/// Create a genesis block, given the initial storage.
pub fn construct_genesis_block(storage: &HashMap<Vec<u8>, Vec<u8>>) -> Block {
let state_root = trie_root(storage.clone().into_iter()).0.into();
let header = Header {
parent_hash: Default::default(),
number: 0,
state_root,
transaction_root: trie_root(vec![].into_iter()).0.into(),
digest: Default::default(),
};
Block {
header,
transactions: vec![],
}
}
#[cfg(test)]
mod tests {
use super::*;
use codec::{Slicable, Joiner};
use runtime_support::{one, two, Hashable};
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use executor::{NativeExecutionDispatch, NativeExecutor, WasmExecutor, with_native_environment,
error};
use state_machine::{execute, Externalities, OverlayedChanges};
use state_machine::backend::InMemory;
use test_runtime::{self, AccountId, Hash, Block, BlockNumber, Header, Digest, Transaction,
UncheckedTransaction};
use ed25519::Pair;
/// A null struct which implements `NativeExecutionDispatch` feeding in the hard-coded runtime.
pub struct LocalNativeExecutionDispatch;
impl NativeExecutionDispatch for LocalNativeExecutionDispatch {
fn native_equivalent() -> &'static [u8] {
// WARNING!!! This assumes that the runtime was built *before* the main project. Until we
// get a proper build script, this must be strictly adhered to or things will go wrong.
include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")
}
fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> error::Result<Vec<u8>> {
with_native_environment(ext, move || test_runtime::apis::dispatch(method, data))?
.ok_or_else(|| error::ErrorKind::MethodNotFound(method.to_owned()).into())
}
}
fn executor() -> NativeExecutor<LocalNativeExecutionDispatch> {
NativeExecutor { _dummy: Default::default() }
}
fn secret_for(who: &AccountId) -> Option<Pair> {
match who {
x if *x == one() => Some(Pair::from_seed(b"12345678901234567890123456789012")),
x if *x == two() => Some("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60".into()),
_ => None,
}
}
fn construct_block(backend: &InMemory, number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
use triehash::ordered_trie_root;
let transactions = txs.into_iter().map(|tx| {
let signature = secret_for(&tx.from).unwrap()
.sign(&tx.encode());
UncheckedTransaction { tx, signature }
}).collect::<Vec<_>>();
let transaction_root = ordered_trie_root(transactions.iter().map(Slicable::encode)).0.into();
let mut header = Header {
parent_hash,
number,
state_root,
transaction_root,
digest: Digest { logs: vec![], },
};
let hash = header.blake2_256();
let mut overlay = OverlayedChanges::default();
for tx in transactions.iter() {
let ret_data = execute(
backend,
&mut overlay,
&executor(),
"execute_transaction",
&vec![].and(&header).and(tx)
).unwrap();
header = Header::decode(&mut &ret_data[..]).unwrap();
}
let ret_data = execute(
backend,
&mut overlay,
&executor(),
"finalise_block",
&vec![].and(&header)
).unwrap();
header = Header::decode(&mut &ret_data[..]).unwrap();
(vec![].and(&Block { header, transactions }), hash.into())
}
fn block1(genesis_hash: Hash, backend: &InMemory) -> (Vec<u8>, Hash) {
construct_block(
backend,
1,
genesis_hash,
hex!("25e5b37074063ab75c889326246640729b40d0c86932edc527bc80db0e04fe5c").into(),
vec![Transaction {
from: one(),
to: two(),
amount: 69,
nonce: 0,
}]
)
}
#[test]
fn construct_genesis_should_work() {
let mut storage = GenesisConfig::new_simple(
vec![one(), two()], 1000
).genesis_map();
let block = construct_genesis_block(&storage);
let genesis_hash = block.header.blake2_256().into();
storage.extend(additional_storage_with_genesis(&block).into_iter());
let mut overlay = OverlayedChanges::default();
let backend = InMemory::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend);
let _ = execute(
&backend,
&mut overlay,
&executor(),
"execute_block",
&b1data
).unwrap();
}
#[test]
#[should_panic]
fn construct_genesis_with_bad_transaction_should_panic() {
let mut storage = GenesisConfig::new_simple(
vec![one(), two()], 68
).genesis_map();
let block = construct_genesis_block(&storage);
let genesis_hash = block.header.blake2_256().into();
storage.extend(additional_storage_with_genesis(&block).into_iter());
let mut overlay = OverlayedChanges::default();
let backend = InMemory::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend);
let _ = execute(
&backend,
&mut overlay,
&executor(),
"execute_block",
&b1data
).unwrap();
}
#[test]
fn construct_genesis_should_work_under_wasm() {
let mut storage = GenesisConfig::new_simple(
vec![one(), two()], 1000
).genesis_map();
let block = construct_genesis_block(&storage);
let genesis_hash = block.header.blake2_256().into();
storage.extend(additional_storage_with_genesis(&block).into_iter());
let mut overlay = OverlayedChanges::default();
let backend = InMemory::from(storage);
let (b1data, _b1hash) = block1(genesis_hash, &backend);
let _ = execute(
&backend,
&mut overlay,
&WasmExecutor,
"execute_block",
&b1data
).unwrap();
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ use primitives::block::{self, HeaderHash};
use blockchain::{self, BlockId, BlockStatus};
fn header_hash(header: &block::Header) -> block::HeaderHash {
primitives::hashing::blake2_256(&ser::to_vec(header)).into()
primitives::hashing::blake2_256(&ser::encode(header)).into()
}
struct PendingBlock {
+5 -1
View File
@@ -22,11 +22,14 @@ extern crate substrate_primitives as primitives;
extern crate substrate_state_machine as state_machine;
extern crate substrate_serializer as ser;
extern crate substrate_codec as codec;
extern crate substrate_executor;
extern crate substrate_executor as executor;
extern crate ed25519;
#[cfg(test)] extern crate substrate_runtime_support as runtime_support;
#[cfg(test)] extern crate substrate_test_runtime as test_runtime;
extern crate triehash;
extern crate parking_lot;
#[cfg(test)] #[macro_use] extern crate hex_literal;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate log;
@@ -34,6 +37,7 @@ pub mod error;
pub mod blockchain;
pub mod backend;
pub mod in_mem;
pub mod genesis;
pub use blockchain::Info as ChainInfo;
pub use blockchain::BlockId;