Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
482a074
reshuffle consensus libraries
rphmeier Feb 8, 2018
917b092
polkadot-useful type definitions for statement table
rphmeier Feb 8, 2018
8e2fd3c
begin BftService
rphmeier Feb 10, 2018
776cf13
Merge branch 'master' into rh-split-bft-table
rphmeier Feb 10, 2018
6abfed4
primary selection logic
rphmeier Feb 12, 2018
fc18524
bft service implementation without I/O
rphmeier Feb 12, 2018
017fd51
extract out `BlockImport` trait
rphmeier Feb 12, 2018
25990ee
Merge branch 'master' into rh-split-bft-table
rphmeier Feb 12, 2018
c33c3ff
allow bft primitives to compile on wasm
rphmeier Feb 12, 2018
acab9a3
Block builder (substrate)
gavofyork Feb 12, 2018
1830fa7
take polkadot-consensus down to the core.
rphmeier Feb 12, 2018
767a9d9
test for preemption
rphmeier Feb 12, 2018
7fc4b4d
fix test build
rphmeier Feb 12, 2018
9acd3f9
Fix wasm build
gavofyork Feb 12, 2018
ca5900f
Bulid on any block
gavofyork Feb 13, 2018
d11cfe1
Test for block builder.
gavofyork Feb 13, 2018
b973ccc
Block import tests for client.
gavofyork Feb 13, 2018
ec61865
Tidy ups
gavofyork Feb 13, 2018
23638cd
clean up block builder instantiation
rphmeier Feb 15, 2018
dda6d24
Merge branch 'rh-split-bft-table' into rh-justification-verification
rphmeier Feb 15, 2018
340ce39
justification verification logic
rphmeier Feb 15, 2018
170b0d1
JustifiedHeader and import
rphmeier Feb 15, 2018
6a1a851
Propert block generation for tests
arkpar Feb 15, 2018
a1247bd
Fixed rpc tests
arkpar Feb 15, 2018
673fc2c
Merge branch 'master' into rh-justification-verification
rphmeier Feb 15, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Block import tests for client.
  • Loading branch information
gavofyork committed Feb 13, 2018
commit b973cccdb7dc3f86383943365b64ece1f9d0aeda
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion substrate/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ ed25519 = { path = "../ed25519" }
substrate-codec = { path = "../codec" }
substrate-executor = { path = "../executor" }
substrate-primitives = { path = "../primitives" }
substrate-runtime-io = { path = "../runtime-io" }
substrate-runtime-support = { path = "../runtime-support" }
substrate-serializer = { path = "../serializer" }
substrate-state-machine = { path = "../state-machine" }
substrate-test-runtime = { path = "../test-runtime" }
substrate-keyring = { path = "../../substrate/keyring" }
14 changes: 8 additions & 6 deletions substrate/client/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,34 @@ use error;
use primitives::block;
use blockchain::{self, BlockId};

/// Block insertion transction. Keeps hold if the inserted block state and data.
/// Block insertion operation. Keeps hold if the inserted block state and data.
pub trait BlockImportOperation {
/// Associated state backend type.
type State: state_machine::backend::Backend;

/// Returns pending state.
fn state(&self) -> error::Result<Self::State>;
fn state(&self) -> error::Result<&Self::State>;
/// Append block data to the transaction.
fn import_block(&mut self, header: block::Header, body: Option<block::Body>, is_new_best: bool) -> error::Result<()>;
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, is_new_best: bool) -> error::Result<()>;
/// Inject storage data into the database.
fn set_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, iter: I) -> error::Result<()>;
/// Inject storage data into the database.
fn reset_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, iter: I) -> error::Result<()>;
}

/// Client backend. Manages the data layer.
pub trait Backend {
/// Associated block insertion transaction type.
/// Associated block insertion operation type.
type BlockImportOperation: BlockImportOperation;
/// Associated blockchain backend type.
type Blockchain: blockchain::Backend;
/// Associated state backend type.
type State: state_machine::backend::Backend;

/// Begin a new block insertion transaction with given parent block id.
fn begin_transaction(&self, block: BlockId) -> error::Result<Self::BlockImportOperation>;
fn begin_operation(&self, block: BlockId) -> error::Result<Self::BlockImportOperation>;
/// Commit block insertion.
fn commit_transaction(&self, transaction: Self::BlockImportOperation) -> error::Result<()>;
fn commit_operation(&self, transaction: Self::BlockImportOperation) -> error::Result<()>;
/// Returns reference to blockchain backend.
fn blockchain(&self) -> &Self::Blockchain;
/// Returns state backend for specified block.
Expand Down
35 changes: 20 additions & 15 deletions substrate/client/src/in_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ use parking_lot::RwLock;
use state_machine;
use error;
use backend;
use primitives;
use ser;
use runtime_support::Hashable;
use primitives::block::{self, HeaderHash};
use blockchain::{self, BlockId, BlockStatus};
use state_machine::backend::Backend as StateBackend;

fn header_hash(header: &block::Header) -> block::HeaderHash {
primitives::hashing::blake2_256(&ser::encode(header)).into()
header.blake2_256().into()
}

struct PendingBlock {
Expand All @@ -41,7 +41,7 @@ struct Block {
body: Option<block::Body>,
}

/// In-memory transaction.
/// In-memory operation.
pub struct BlockImportOperation {
pending_block: Option<PendingBlock>,
pending_state: state_machine::backend::InMemory,
Expand Down Expand Up @@ -156,12 +156,12 @@ impl blockchain::Backend for Blockchain {
impl backend::BlockImportOperation for BlockImportOperation {
type State = state_machine::backend::InMemory;

fn state(&self) -> error::Result<Self::State> {
Ok(self.pending_state.clone())
fn state(&self) -> error::Result<&Self::State> {
Ok(&self.pending_state)
}

fn import_block(&mut self, header: block::Header, body: Option<block::Body>, is_new_best: bool) -> error::Result<()> {
assert!(self.pending_block.is_none(), "Only one block per transaction is allowed");
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, is_new_best: bool) -> error::Result<()> {
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
self.pending_block = Some(PendingBlock {
block: Block {
header: header,
Expand All @@ -172,6 +172,11 @@ impl backend::BlockImportOperation for BlockImportOperation {
Ok(())
}

fn set_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, changes: I) -> error::Result<()> {
self.pending_state.commit(changes);
Ok(())
}

fn reset_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, iter: I) -> error::Result<()> {
self.pending_state = state_machine::backend::InMemory::from(iter.collect());
Ok(())
Expand Down Expand Up @@ -214,10 +219,10 @@ impl Backend {
};
edit_header(&mut header);

let mut tx = self.begin_transaction(BlockId::Hash(best_hash)).expect("In-memory backend does not fail");
let mut tx = self.begin_operation(BlockId::Hash(best_hash)).expect("In-memory backend does not fail");
best_hash = header_hash(&header);
tx.import_block(header, None, true).expect("In-memory backend does not fail");
self.commit_transaction(tx).expect("In-memory backend does not fail");
tx.set_block_data(header, Some(vec![]), true).expect("In-memory backend does not fail");
self.commit_operation(tx).expect("In-memory backend does not fail");
}
}

Expand All @@ -232,7 +237,7 @@ impl backend::Backend for Backend {
type Blockchain = Blockchain;
type State = state_machine::backend::InMemory;

fn begin_transaction(&self, block: BlockId) -> error::Result<Self::BlockImportOperation> {
fn begin_operation(&self, block: BlockId) -> error::Result<Self::BlockImportOperation> {
let state = match block {
BlockId::Hash(h) if h.is_zero() => Self::State::default(),
_ => self.state_at(block)?,
Expand All @@ -244,10 +249,10 @@ impl backend::Backend for Backend {
})
}

fn commit_transaction(&self, transaction: Self::BlockImportOperation) -> error::Result<()> {
if let Some(pending_block) = transaction.pending_block {
fn commit_operation(&self, operation: Self::BlockImportOperation) -> error::Result<()> {
if let Some(pending_block) = operation.pending_block {
let hash = header_hash(&pending_block.block.header);
self.states.write().insert(hash, transaction.pending_state);
self.states.write().insert(hash, operation.pending_state);
self.blockchain.insert(hash, pending_block.block.header, pending_block.block.body, pending_block.is_best);
}
Ok(())
Expand Down
124 changes: 112 additions & 12 deletions substrate/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@

#![warn(missing_docs)]

extern crate substrate_runtime_support as runtime_support;
extern crate substrate_runtime_io as runtime_io;
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;
#[cfg(test)] #[macro_use] 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;
#[cfg(test)] extern crate substrate_keyring as keyring;

Expand All @@ -51,6 +51,7 @@ use codec::{KeyedVec, Slicable};
use blockchain::Backend as BlockchainBackend;
use backend::BlockImportOperation;
use state_machine::backend::Backend as StateBackend;
use state_machine::{Ext, OverlayedChanges};

/// Polkadot Client
#[derive(Debug)]
Expand Down Expand Up @@ -136,10 +137,10 @@ impl<B, E> Client<B, E> where
if backend.blockchain().header(BlockId::Number(0))?.is_none() {
trace!("Empty database, writing genesis block");
let (genesis_header, genesis_store) = build_genesis();
let mut tx = backend.begin_transaction(BlockId::Hash(block::HeaderHash::default()))?;
tx.reset_storage(genesis_store.into_iter())?;
tx.import_block(genesis_header, None, true)?;
backend.commit_transaction(tx)?;
let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?;
op.reset_storage(genesis_store.into_iter())?;
op.set_block_data(genesis_header, Some(vec![]), true)?;
backend.commit_operation(op)?;
}
Ok(Client {
backend,
Expand Down Expand Up @@ -200,6 +201,23 @@ impl<B, E> Client<B, E> where
Ok(CallResult { return_data, changes })
}

/// Set up the native execution environment to call into a native runtime code.
pub fn using_environment<F: FnOnce() -> T, T>(
&self, f: F
) -> error::Result<T> {
self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f)
}

/// Set up the native execution environment to call into a native runtime code.
pub fn using_environment_at<F: FnOnce() -> T, T>(
&self,
id: &BlockId,
overlay: &mut OverlayedChanges,
f: F
) -> error::Result<T> {
Ok(runtime_io::with_externalities(&mut Ext { backend: &self.state_at(id)?, overlay }, f))
}

/// Create a new block, built on the head of the chain.
pub fn new_block(&self) -> error::Result<block_builder::BlockBuilder<B, E>> where E: Clone {
block_builder::BlockBuilder::new(self)
Expand All @@ -219,13 +237,21 @@ impl<B, E> Client<B, E> where
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
}

let mut transaction = self.backend.begin_transaction(BlockId::Hash(header.parent_hash))?;
let mut _state = transaction.state()?;
// TODO: execute block on _state
let mut transaction = self.backend.begin_operation(BlockId::Hash(header.parent_hash))?;
let mut overlay = OverlayedChanges::default();

state_machine::execute(
transaction.state()?,
&mut overlay,
&self.executor,
"execute_block",
&block::Block { header: header.clone(), transactions: body.clone().unwrap_or_default().clone() }.encode()
)?;

let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1;
transaction.import_block(header, body, is_new_best)?;
self.backend.commit_transaction(transaction)?;
transaction.set_block_data(header, body, is_new_best)?;
transaction.set_storage(overlay.drain())?;
self.backend.commit_operation(transaction)?;
Ok(ImportResult::Queued)
}

Expand Down Expand Up @@ -284,12 +310,37 @@ impl<B, E> Client<B, E> where
mod tests {
use super::*;
use codec::Slicable;
use runtime_support::Hashable;
use keyring::Keyring;
use primitives::block::Transaction as PrimitiveTransaction;
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use test_runtime::{UncheckedTransaction, Transaction};
use test_runtime;

native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));

fn genesis_config() -> GenesisConfig {
GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000)
}

fn prepare_genesis() -> (primitives::block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
let mut storage = genesis_config().genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
}

#[test]
fn client_initialises_from_genesis_ok() {
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();

assert_eq!(client.block_hash_from_id(&BlockId::Number(0)).unwrap().unwrap().0, prepare_genesis().0.blake2_256());
}

#[test]
fn authorities_call_works() {
let genesis_config = GenesisConfig::new_simple(vec![
Expand All @@ -315,7 +366,7 @@ mod tests {
}

#[test]
fn block_builder_works() {
fn block_builder_works_with_no_transactions() {
let genesis_config = GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Expand All @@ -330,10 +381,59 @@ mod tests {
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();

let genesis_hash = genesis::construct_genesis_block(&genesis_config.genesis_map()).header.blake2_256();
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap().0, genesis_hash);

let builder = client.new_block().unwrap();
let block = builder.bake().unwrap();

assert_eq!(block.header.parent_hash.0, genesis_hash);

client.import_block(block.header, Some(block.transactions)).unwrap();

assert_eq!(client.info().unwrap().chain.best_number, 1);
}

trait Signable {
fn signed(self) -> PrimitiveTransaction;
}
impl Signable for Transaction {
fn signed(self) -> PrimitiveTransaction {
let signature = Keyring::from_raw_public(self.from.clone()).unwrap().sign(&self.encode());
PrimitiveTransaction::decode(&mut UncheckedTransaction { signature, tx: self }.encode().as_ref()).unwrap()
}
}

#[test]
fn block_builder_works_with_transactions() {
let genesis_config = GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000);

let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();

let mut builder = client.new_block().unwrap();

builder.push(Transaction {
from: Keyring::Alice.to_raw_public(),
to: Keyring::Ferdie.to_raw_public(),
amount: 42,
nonce: 0
}.signed()).unwrap();
let block = builder.bake().unwrap();
client.import_block(block.header, Some(block.transactions)).unwrap();

assert_eq!(client.info().unwrap().chain.best_number, 1);
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 958);
assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 42);
}
}
2 changes: 1 addition & 1 deletion substrate/state-machine/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl error::Error for Void {

/// In-memory backend. Fully recomputes tries on each commit but useful for
/// tests.
#[derive(Default, Clone)]
#[derive(Debug, PartialEq, Default, Clone)]
pub struct InMemory {
inner: MemoryState, // keeps all the state in memory.
}
Expand Down
Loading