diff --git a/substrate/substrate/client/src/client.rs b/substrate/substrate/client/src/client.rs new file mode 100644 index 0000000000..3fff8bf2ee --- /dev/null +++ b/substrate/substrate/client/src/client.rs @@ -0,0 +1,410 @@ +// 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 . + +//! Substrate Client + +use primitives::{block, AuthorityId}; +use primitives::storage::{StorageKey, StorageData}; +use codec::{KeyedVec, Slicable}; +use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor}; + +use backend::{self, BlockImportOperation}; +use blockchain::{self, Info as ChainInfo, BlockId, Backend as ChainBackend}; +use {error, in_mem, block_builder, runtime_io}; + +/// Polkadot Client +#[derive(Debug)] +pub struct Client where B: backend::Backend { + backend: B, + executor: E, +} + +/// Client info +// TODO: split queue info from chain info and amalgamate into single struct. +#[derive(Debug)] +pub struct ClientInfo { + /// Best block hash. + pub chain: ChainInfo, + /// Best block number in the queue. + pub best_queued_number: Option, + /// Best queued block hash. + pub best_queued_hash: Option, +} + +/// Information regarding the result of a call. +pub struct CallResult { + /// The data that was returned from the call. + pub return_data: Vec, + /// The changes made to the state by the call. + pub changes: OverlayedChanges, +} + +/// Block import result. +#[derive(Debug)] +pub enum ImportResult { + /// Added to the import queue. + Queued, + /// Already in the import queue. + AlreadyQueued, + /// Already in the blockchain. + AlreadyInChain, + /// Block or parent is known to be bad. + KnownBad, + /// Block parent is not in the chain. + UnknownParent, +} + +/// Block status. +#[derive(Debug, PartialEq, Eq)] +pub enum BlockStatus { + /// Added to the import queue. + Queued, + /// Already in the blockchain. + InChain, + /// Block or parent is known to be bad. + KnownBad, + /// Not in the queue or the blockchain. + Unknown, +} + +/// Create an instance of in-memory client. +pub fn new_in_mem( + executor: E, + build_genesis: F +) -> error::Result> + where + E: CodeExecutor, + F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) +{ + Client::new(in_mem::Backend::new(), executor, build_genesis) +} + +impl Client where + B: backend::Backend, + E: CodeExecutor, + error::Error: From<<::State as StateBackend>::Error>, +{ + /// Creates new Polkadot Client with given blockchain and code executor. + pub fn new( + backend: B, + executor: E, + build_genesis: F + ) -> error::Result + where + F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) + { + if backend.blockchain().header(BlockId::Number(0))?.is_none() { + trace!("Empty database, writing genesis block"); + let (genesis_header, genesis_store) = build_genesis(); + 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, + executor, + }) + } + + /// Get a reference to the state at a given block. + pub fn state_at(&self, block: &BlockId) -> error::Result { + self.backend.state_at(*block) + } + + /// Expose backend reference. To be used in tests only + pub fn backend(&self) -> &B { + &self.backend + } + + /// Return single storage entry of contract under given address in state in a block of given hash. + pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result { + Ok(self.state_at(id)? + .storage(&key.0) + .map(|x| StorageData(x.to_vec()))?) + } + + /// Get the code at a given block. + pub fn code_at(&self, id: &BlockId) -> error::Result> { + self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0) + } + + /// Clone a new instance of Executor. + pub fn clone_executor(&self) -> E where E: Clone { + self.executor.clone() + } + + /// Get the current set of authorities from storage. + pub fn authorities_at(&self, id: &BlockId) -> error::Result> { + let state = self.state_at(id)?; + (0..u32::decode(&mut state.storage(b":auth:len")?).ok_or(error::ErrorKind::AuthLen)?) + .map(|i| state.storage(&i.to_keyed_vec(b":auth:")) + .map_err(|_| error::ErrorKind::Backend) + .and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::Auth(i))) + .map_err(Into::into) + ).collect() + } + + /// Execute a call to a contract on top of state in a block of given hash. + /// + /// No changes are made. + pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result { + let mut changes = OverlayedChanges::default(); + let return_data = state_machine::execute( + &self.state_at(id)?, + &mut changes, + &self.executor, + method, + call_data, + )?; + Ok(CallResult { return_data, changes }) + } + + /// Set up the native execution environment to call into a native runtime code. + pub fn using_environment T, T>( + &self, f: F + ) -> error::Result { + 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 T, T>( + &self, + id: &BlockId, + overlay: &mut OverlayedChanges, + f: F + ) -> error::Result { + 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> where E: Clone { + block_builder::BlockBuilder::new(self) + } + + /// Create a new block, built on top of `parent`. + pub fn new_block_at(&self, parent: &BlockId) -> error::Result> where E: Clone { + block_builder::BlockBuilder::at_block(parent, &self) + } + + /// Queue a block for import. + pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { + // TODO: import lock + // TODO: validate block + match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? { + blockchain::BlockStatus::InChain => (), + blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), + } + + 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.set_block_data(header, body, is_new_best)?; + transaction.set_storage(overlay.drain())?; + self.backend.commit_operation(transaction)?; + Ok(ImportResult::Queued) + } + + /// Get blockchain info. + pub fn info(&self) -> error::Result { + let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; + Ok(ClientInfo { + chain: info, + best_queued_hash: None, + best_queued_number: None, + }) + } + + /// Get block status. + pub fn block_status(&self, id: &BlockId) -> error::Result { + // TODO: more efficient implementation + match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { + true => Ok(BlockStatus::InChain), + false => Ok(BlockStatus::Unknown), + } + } + + /// Get block hash by number. + pub fn block_hash(&self, block_number: block::Number) -> error::Result> { + self.backend.blockchain().hash(block_number) + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(h) => Ok(Some(h)), + BlockId::Number(n) => self.block_hash(n), + } + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_number_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)), + BlockId::Number(n) => Ok(Some(n)), + } + } + + /// Get block header by id. + pub fn header(&self, id: &BlockId) -> error::Result> { + self.backend.blockchain().header(*id) + } + + /// Get block body by id. + pub fn body(&self, id: &BlockId) -> error::Result> { + self.backend.blockchain().body(*id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use codec::Slicable; + use keyring::Keyring; + use {primitives, genesis}; + 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, Vec)>) { + 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(); + let genesis_hash = client.block_hash(0).unwrap().unwrap(); + + assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0); + } + + #[test] + fn authorities_call_works() { + 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(); + + assert_eq!(client.info().unwrap().chain.best_number, 0); + assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ]); + } + + #[test] + fn block_builder_works_with_no_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 builder = client.new_block().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_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap()); + } + + 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); + } +} diff --git a/substrate/substrate/client/src/lib.rs b/substrate/substrate/client/src/lib.rs index 947732ee80..44b44613c8 100644 --- a/substrate/substrate/client/src/lib.rs +++ b/substrate/substrate/client/src/lib.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Substrate Client +//! Substrate Client and associated logic. #![warn(missing_docs)] @@ -40,398 +40,7 @@ pub mod backend; pub mod in_mem; pub mod genesis; pub mod block_builder; +mod client; -pub use blockchain::Info as ChainInfo; -pub use blockchain::BlockId; - -use primitives::{block, AuthorityId}; -use primitives::storage::{StorageKey, StorageData}; -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)] -pub struct Client where B: backend::Backend { - backend: B, - executor: E, -} - -/// Client info -// TODO: split queue info from chain info and amalgamate into single struct. -#[derive(Debug)] -pub struct ClientInfo { - /// Best block hash. - pub chain: ChainInfo, - /// Best block number in the queue. - pub best_queued_number: Option, - /// Best queued block hash. - pub best_queued_hash: Option, -} - -/// Information regarding the result of a call. -pub struct CallResult { - /// The data that was returned from the call. - pub return_data: Vec, - /// The changes made to the state by the call. - pub changes: state_machine::OverlayedChanges, -} - -/// Block import result. -#[derive(Debug)] -pub enum ImportResult { - /// Added to the import queue. - Queued, - /// Already in the import queue. - AlreadyQueued, - /// Already in the blockchain. - AlreadyInChain, - /// Block or parent is known to be bad. - KnownBad, - /// Block parent is not in the chain. - UnknownParent, -} - -/// Block status. -#[derive(Debug, PartialEq, Eq)] -pub enum BlockStatus { - /// Added to the import queue. - Queued, - /// Already in the blockchain. - InChain, - /// Block or parent is known to be bad. - KnownBad, - /// Not in the queue or the blockchain. - Unknown, -} - -/// Create an instance of in-memory client. -pub fn new_in_mem( - executor: E, - build_genesis: F -) -> error::Result> - where - E: state_machine::CodeExecutor, - F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) -{ - Client::new(in_mem::Backend::new(), executor, build_genesis) -} - -impl Client where - B: backend::Backend, - E: state_machine::CodeExecutor, - error::Error: From<<::State as state_machine::backend::Backend>::Error>, -{ - /// Creates new Polkadot Client with given blockchain and code executor. - pub fn new( - backend: B, - executor: E, - build_genesis: F - ) -> error::Result - where - F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) - { - if backend.blockchain().header(BlockId::Number(0))?.is_none() { - trace!("Empty database, writing genesis block"); - let (genesis_header, genesis_store) = build_genesis(); - 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, - executor, - }) - } - - /// Get a reference to the state at a given block. - pub fn state_at(&self, block: &BlockId) -> error::Result { - self.backend.state_at(*block) - } - - /// Expose backend reference. To be used in tests only - pub fn backend(&self) -> &B { - &self.backend - } - - /// Return single storage entry of contract under given address in state in a block of given hash. - pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result { - Ok(self.state_at(id)? - .storage(&key.0) - .map(|x| StorageData(x.to_vec()))?) - } - - /// Get the code at a given block. - pub fn code_at(&self, id: &BlockId) -> error::Result> { - self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0) - } - - /// Clone a new instance of Executor. - pub fn clone_executor(&self) -> E where E: Clone { - self.executor.clone() - } - - /// Get the current set of authorities from storage. - pub fn authorities_at(&self, id: &BlockId) -> error::Result> { - let state = self.state_at(id)?; - (0..u32::decode(&mut state.storage(b":auth:len")?).ok_or(error::ErrorKind::AuthLen)?) - .map(|i| state.storage(&i.to_keyed_vec(b":auth:")) - .map_err(|_| error::ErrorKind::Backend) - .and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::Auth(i))) - .map_err(Into::into) - ).collect() - } - - /// Execute a call to a contract on top of state in a block of given hash. - /// - /// No changes are made. - pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result { - let mut changes = state_machine::OverlayedChanges::default(); - let return_data = state_machine::execute( - &self.state_at(id)?, - &mut changes, - &self.executor, - method, - call_data, - )?; - Ok(CallResult { return_data, changes }) - } - - /// Set up the native execution environment to call into a native runtime code. - pub fn using_environment T, T>( - &self, f: F - ) -> error::Result { - 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 T, T>( - &self, - id: &BlockId, - overlay: &mut OverlayedChanges, - f: F - ) -> error::Result { - 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> where E: Clone { - block_builder::BlockBuilder::new(self) - } - - /// Create a new block, built on top of `parent`. - pub fn new_block_at(&self, parent: &BlockId) -> error::Result> where E: Clone { - block_builder::BlockBuilder::at_block(parent, &self) - } - - /// Queue a block for import. - pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { - // TODO: import lock - // TODO: validate block - match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? { - blockchain::BlockStatus::InChain => (), - blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), - } - - 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.set_block_data(header, body, is_new_best)?; - transaction.set_storage(overlay.drain())?; - self.backend.commit_operation(transaction)?; - Ok(ImportResult::Queued) - } - - /// Get blockchain info. - pub fn info(&self) -> error::Result { - let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; - Ok(ClientInfo { - chain: info, - best_queued_hash: None, - best_queued_number: None, - }) - } - - /// Get block status. - pub fn block_status(&self, id: &BlockId) -> error::Result { - // TODO: more efficient implementation - match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { - true => Ok(BlockStatus::InChain), - false => Ok(BlockStatus::Unknown), - } - } - - /// Get block hash by number. - pub fn block_hash(&self, block_number: block::Number) -> error::Result> { - self.backend.blockchain().hash(block_number) - } - - /// Convert an arbitrary block ID into a block hash. - pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { - match *id { - BlockId::Hash(h) => Ok(Some(h)), - BlockId::Number(n) => self.block_hash(n), - } - } - - /// Convert an arbitrary block ID into a block hash. - pub fn block_number_from_id(&self, id: &BlockId) -> error::Result> { - match *id { - BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)), - BlockId::Number(n) => Ok(Some(n)), - } - } - - /// Get block header by id. - pub fn header(&self, id: &BlockId) -> error::Result> { - self.backend.blockchain().header(*id) - } - - /// Get block body by id. - pub fn body(&self, id: &BlockId) -> error::Result> { - self.backend.blockchain().body(*id) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use codec::Slicable; - 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, Vec)>) { - 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(); - let genesis_hash = client.block_hash(0).unwrap().unwrap(); - - assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0); - } - - #[test] - fn authorities_call_works() { - 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(); - - assert_eq!(client.info().unwrap().chain.best_number, 0); - assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ]); - } - - #[test] - fn block_builder_works_with_no_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 builder = client.new_block().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_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap()); - } - - 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); - } -} +pub use client::{Client, ClientInfo, CallResult, ImportResult, BlockStatus, new_in_mem}; +pub use blockchain::{Info as ChainInfo, BlockId}; diff --git a/substrate/substrate/state-machine/src/backend.rs b/substrate/substrate/state-machine/src/backend.rs index f77e9a59d8..6174649c5c 100644 --- a/substrate/substrate/state-machine/src/backend.rs +++ b/substrate/substrate/state-machine/src/backend.rs @@ -20,9 +20,6 @@ use std::{error, fmt}; use super::{Update, MemoryState}; -/// Output of a commit. -pub struct Committed {} - /// A state backend is used to read state data and can have changes committed /// to it. pub trait Backend { @@ -33,7 +30,7 @@ pub trait Backend { fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; /// Commit updates to the backend and get new state. - fn commit(&mut self, changes: I) -> Committed + fn commit(&mut self, changes: I) where I: IntoIterator; /// Get all key/value pairs into a Vec. @@ -80,11 +77,10 @@ impl Backend for InMemory { Ok(self.inner.storage(key).unwrap_or(&[])) } - fn commit(&mut self, changes: I) -> Committed + fn commit(&mut self, changes: I) where I: IntoIterator { self.inner.update(changes); - Committed {} } fn pairs(&self) -> Vec<(&[u8], &[u8])> { diff --git a/substrate/substrate/state-machine/src/lib.rs b/substrate/substrate/state-machine/src/lib.rs index a3f2e3dc4b..07d9683b3d 100644 --- a/substrate/substrate/state-machine/src/lib.rs +++ b/substrate/substrate/state-machine/src/lib.rs @@ -40,6 +40,7 @@ mod testing; pub use testing::TestExternalities; pub use ext::Ext; +pub use backend::Backend; /// Updates to be committed to the state. pub type Update = (Vec, Vec);