diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 6e54ac6c5e..cda0cd782e 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1391,8 +1391,8 @@ dependencies = [ "substrate-executor 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", "substrate-runtime-support 0.1.0", - "substrate-serializer 0.1.0", "substrate-state-machine 0.1.0", "substrate-test-runtime 0.1.0", "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/substrate/substrate/client/Cargo.toml b/substrate/substrate/client/Cargo.toml index b9e0135b9e..8d3b419991 100644 --- a/substrate/substrate/client/Cargo.toml +++ b/substrate/substrate/client/Cargo.toml @@ -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" } diff --git a/substrate/substrate/client/src/backend.rs b/substrate/substrate/client/src/backend.rs index fad0f203e8..4421c82b37 100644 --- a/substrate/substrate/client/src/backend.rs +++ b/substrate/substrate/client/src/backend.rs @@ -21,22 +21,24 @@ 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; + fn state(&self) -> error::Result<&Self::State>; /// Append block data to the transaction. - fn import_block(&mut self, header: block::Header, body: Option, is_new_best: bool) -> error::Result<()>; + fn set_block_data(&mut self, header: block::Header, body: Option, is_new_best: bool) -> error::Result<()>; + /// Inject storage data into the database. + fn set_storage, Vec)>>(&mut self, iter: I) -> error::Result<()>; /// Inject storage data into the database. fn reset_storage, Vec)>>(&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; @@ -44,9 +46,9 @@ pub trait Backend { 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; + fn begin_operation(&self, block: BlockId) -> error::Result; /// 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. diff --git a/substrate/substrate/client/src/in_mem.rs b/substrate/substrate/client/src/in_mem.rs index ce1aaf3db5..69afbb10b6 100644 --- a/substrate/substrate/client/src/in_mem.rs +++ b/substrate/substrate/client/src/in_mem.rs @@ -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 { @@ -41,7 +41,7 @@ struct Block { body: Option, } -/// In-memory transaction. +/// In-memory operation. pub struct BlockImportOperation { pending_block: Option, pending_state: state_machine::backend::InMemory, @@ -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 { - 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, 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, 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, @@ -172,6 +172,11 @@ impl backend::BlockImportOperation for BlockImportOperation { Ok(()) } + fn set_storage, Vec)>>(&mut self, changes: I) -> error::Result<()> { + self.pending_state.commit(changes); + Ok(()) + } + fn reset_storage, Vec)>>(&mut self, iter: I) -> error::Result<()> { self.pending_state = state_machine::backend::InMemory::from(iter.collect()); Ok(()) @@ -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"); } } @@ -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 { + fn begin_operation(&self, block: BlockId) -> error::Result { let state = match block { BlockId::Hash(h) if h.is_zero() => Self::State::default(), _ => self.state_at(block)?, @@ -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(()) diff --git a/substrate/substrate/client/src/lib.rs b/substrate/substrate/client/src/lib.rs index 046d84de5f..62df539065 100644 --- a/substrate/substrate/client/src/lib.rs +++ b/substrate/substrate/client/src/lib.rs @@ -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; @@ -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)] @@ -136,10 +137,10 @@ impl Client 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, @@ -200,6 +201,23 @@ impl Client where 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) @@ -219,13 +237,21 @@ impl Client 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) } @@ -284,12 +310,37 @@ impl Client 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, 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(); + + 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![ @@ -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(), @@ -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); + } } diff --git a/substrate/substrate/state-machine/src/backend.rs b/substrate/substrate/state-machine/src/backend.rs index a1dec6dc3a..f77e9a59d8 100644 --- a/substrate/substrate/state-machine/src/backend.rs +++ b/substrate/substrate/state-machine/src/backend.rs @@ -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. } diff --git a/substrate/substrate/state-machine/src/lib.rs b/substrate/substrate/state-machine/src/lib.rs index 33dd346c1c..a3f2e3dc4b 100644 --- a/substrate/substrate/state-machine/src/lib.rs +++ b/substrate/substrate/state-machine/src/lib.rs @@ -42,13 +42,10 @@ pub use testing::TestExternalities; pub use ext::Ext; /// Updates to be committed to the state. -pub enum Update { - /// Set storage of object at given key -- empty is deletion. - Storage(Vec, Vec), -} +pub type Update = (Vec, Vec); // in-memory section of the state. -#[derive(Default, Clone)] +#[derive(Debug, PartialEq, Default, Clone)] struct MemoryState { storage: HashMap, Vec>, } @@ -63,15 +60,11 @@ impl MemoryState { } fn update(&mut self, changes: I) where I: IntoIterator { - for update in changes { - match update { - Update::Storage(key, val) => { - if val.is_empty() { - self.storage.remove(&key); - } else { - self.storage.insert(key, val); - } - } + for (key, val) in changes { + if val.is_empty() { + self.storage.remove(&key); + } else { + self.storage.insert(key, val); } } } @@ -105,10 +98,12 @@ impl OverlayedChanges { /// Commit prospective changes to state. pub fn commit_prospective(&mut self) { - let storage_updates = self.prospective.storage.drain() - .map(|(key, value)| Update::Storage(key, value)); + self.committed.update(self.prospective.storage.drain()); + } - self.committed.update(storage_updates); + /// Drain prospective changes to an iterator. + pub fn drain(&mut self) -> ::std::collections::hash_map::Drain, std::vec::Vec> { + self.committed.storage.drain() } } diff --git a/substrate/substrate/test-runtime/src/genesismap.rs b/substrate/substrate/test-runtime/src/genesismap.rs index 0dc01c0abc..1bc24d2657 100644 --- a/substrate/substrate/test-runtime/src/genesismap.rs +++ b/substrate/substrate/test-runtime/src/genesismap.rs @@ -62,7 +62,10 @@ macro_rules! map { pub fn additional_storage_with_genesis(genesis_block: &Block) -> HashMap, Vec> { use codec::Slicable; + use primitives::hexdisplay::HexDisplay; + println!("genesis hash {}", HexDisplay::from(&genesis_block.header.blake2_256())); + println!("genesis {}", HexDisplay::from(&genesis_block.header.encode())); map![ - twox_128(&b"latest"[..]).encode() => genesis_block.header.blake2_256().encode() + twox_128(&b"latest"[..]).to_vec() => genesis_block.header.blake2_256().to_vec() ] }