diff --git a/polkadot/executor/src/lib.rs b/polkadot/executor/src/lib.rs index a7cef0c3cb337..f7ded050250b9 100644 --- a/polkadot/executor/src/lib.rs +++ b/polkadot/executor/src/lib.rs @@ -73,9 +73,9 @@ mod tests { #[test] fn panic_execution_with_foreign_code_gives_error() { let one = Keyring::One.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); assert!(r.is_err()); @@ -84,9 +84,9 @@ mod tests { #[test] fn panic_execution_with_native_equivalent_code_gives_error() { let one = Keyring::One.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); assert!(r.is_err()); @@ -97,9 +97,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); assert!(r.is_ok()); @@ -115,9 +115,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); assert!(r.is_ok()); @@ -133,7 +133,7 @@ mod tests { let two = Keyring::Two.to_raw_public(); let three = [3u8; 32]; - TestExternalities { storage: map![ + map![ twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].encode(), twox_128(b"gov:apr").to_vec() => vec![].and(&667u32), twox_128(b"ses:len").to_vec() => vec![].and(&2u64), @@ -149,7 +149,7 @@ mod tests { twox_128(b"sta:vac").to_vec() => vec![].and(&3u64), twox_128(b"sta:era").to_vec() => vec![].and(&0u64), twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], } + ] } fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec) -> (Vec, Hash) { @@ -250,9 +250,9 @@ mod tests { #[test] fn panic_execution_gives_error() { let one = Keyring::One.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm"); let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); @@ -264,9 +264,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm"); let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); diff --git a/polkadot/runtime/src/runtime/governance.rs b/polkadot/runtime/src/runtime/governance.rs index b8733b9776a3b..9138a3eddfee7 100644 --- a/polkadot/runtime/src/runtime/governance.rs +++ b/polkadot/runtime/src/runtime/governance.rs @@ -157,7 +157,7 @@ mod tests { let two = Keyring::Two.to_raw_public(); let three = [3u8; 32]; - TestExternalities { storage: map![ + map![ twox_128(APPROVALS_REQUIRED).to_vec() => vec![].and(&667u32), twox_128(b"ses:len").to_vec() => vec![].and(&1u64), twox_128(b"ses:val:len").to_vec() => vec![].and(&3u32), @@ -171,7 +171,7 @@ mod tests { twox_128(b"sta:spe").to_vec() => vec![].and(&1u64), twox_128(b"sta:vac").to_vec() => vec![].and(&3u64), twox_128(b"sta:era").to_vec() => vec![].and(&1u64) - ], } + ] } #[test] diff --git a/polkadot/runtime/src/runtime/parachains.rs b/polkadot/runtime/src/runtime/parachains.rs index dd7485ddc83e8..1cd4151cf9afc 100644 --- a/polkadot/runtime/src/runtime/parachains.rs +++ b/polkadot/runtime/src/runtime/parachains.rs @@ -83,10 +83,10 @@ mod tests { use runtime::{consensus, session}; fn simple_setup() -> TestExternalities { - TestExternalities { storage: map![ + map![ twox_128(b"ses:val:len").to_vec() => vec![].and(&8u32), twox_128(b"par:cou").to_vec() => vec![].and(&2u32) - ], } + ] } #[test] diff --git a/polkadot/runtime/src/runtime/session.rs b/polkadot/runtime/src/runtime/session.rs index a3a43d88cc131..e9bf5f3765640 100644 --- a/polkadot/runtime/src/runtime/session.rs +++ b/polkadot/runtime/src/runtime/session.rs @@ -145,7 +145,7 @@ mod tests { use runtime::{consensus, session}; fn simple_setup() -> TestExternalities { - TestExternalities { storage: map![ + map![ twox_128(SESSION_LENGTH).to_vec() => vec![].and(&2u64), // the validators (10, 20, ...) twox_128(b"ses:val:len").to_vec() => vec![].and(&2u32), @@ -155,7 +155,7 @@ mod tests { b":auth:len".to_vec() => vec![].and(&2u32), 0u32.to_keyed_vec(b":auth:") => vec![11; 32], 1u32.to_keyed_vec(b":auth:") => vec![21; 32] - ], } + ] } #[test] diff --git a/polkadot/runtime/src/runtime/staking.rs b/polkadot/runtime/src/runtime/staking.rs index a66d43eeb9037..0bb4372c6d269 100644 --- a/polkadot/runtime/src/runtime/staking.rs +++ b/polkadot/runtime/src/runtime/staking.rs @@ -227,7 +227,7 @@ mod tests { let three = [3u8; 32]; let four = [4u8; 32]; - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(b"ses:len").to_vec() => vec![].and(&1u64), twox_128(b"ses:val:len").to_vec() => vec![].and(&2u32), twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32], @@ -239,7 +239,7 @@ mod tests { twox_128(&two.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&20u64), twox_128(&three.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&30u64), twox_128(&four.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&40u64) - ], }; + ]; with_externalities(&mut t, || { assert_eq!(era_length(), 2u64); @@ -296,10 +296,10 @@ mod tests { #[test] fn staking_eras_work() { - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(b"ses:len").to_vec() => vec![].and(&1u64), twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&2u64) - ], }; + ]; with_externalities(&mut t, || { assert_eq!(era_length(), 2u64); assert_eq!(sessions_per_era(), 2u64); @@ -363,9 +363,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&42u64) - ], }; + ]; with_externalities(&mut t, || { assert_eq!(balance(&one), 42); @@ -378,9 +378,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64) - ], }; + ]; with_externalities(&mut t, || { transfer(&one, &two, 69); @@ -395,9 +395,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64) - ], }; + ]; with_externalities(&mut t, || { stake(&one); diff --git a/polkadot/runtime/src/runtime/system.rs b/polkadot/runtime/src/runtime/system.rs index 2f8f5c25c7124..7d5e8835ed05c 100644 --- a/polkadot/runtime/src/runtime/system.rs +++ b/polkadot/runtime/src/runtime/system.rs @@ -247,9 +247,9 @@ mod tests { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], }; + ]; let tx = UncheckedTransaction { transaction: Transaction { @@ -272,7 +272,7 @@ mod tests { let two = Keyring::Two.to_raw_public(); let three = [3u8; 32]; - TestExternalities { storage: map![ + map![ twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].encode(), twox_128(b"gov:apr").to_vec() => vec![].and(&667u32), twox_128(b"ses:len").to_vec() => vec![].and(&2u64), @@ -288,7 +288,7 @@ mod tests { twox_128(b"sta:vac").to_vec() => vec![].and(&3u64), twox_128(b"sta:era").to_vec() => vec![].and(&0u64), twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], } + ] } #[test] diff --git a/polkadot/runtime/src/runtime/timestamp.rs b/polkadot/runtime/src/runtime/timestamp.rs index c64a511ad0388..0b96f39bd4a8c 100644 --- a/polkadot/runtime/src/runtime/timestamp.rs +++ b/polkadot/runtime/src/runtime/timestamp.rs @@ -47,9 +47,9 @@ mod tests { #[test] fn timestamp_works() { - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = map![ twox_128(CURRENT_TIMESTAMP).to_vec() => vec![].and(&42u64) - ], }; + ]; with_externalities(&mut t, || { assert_eq!(get(), 42); diff --git a/polkadot/runtime/wasm/genesis.wasm b/polkadot/runtime/wasm/genesis.wasm index 91c7b475743cb..bc0cb89235c57 100644 Binary files a/polkadot/runtime/wasm/genesis.wasm and b/polkadot/runtime/wasm/genesis.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index 84e52dbc50cfa..a1f28badf2e10 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index a92614dcc2d6f..160550b5c786f 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/substrate/client/src/backend.rs b/substrate/client/src/backend.rs index eef904679173c..b53f48ff49d4b 100644 --- a/substrate/client/src/backend.rs +++ b/substrate/client/src/backend.rs @@ -31,7 +31,7 @@ pub trait BlockImportOperation { /// Append block data to the transaction. fn set_block_data(&mut self, header: block::Header, body: Option, justification: Option, is_new_best: bool) -> error::Result<()>; /// Inject storage data into the database. - fn set_storage, Vec)>>(&mut self, iter: I) -> error::Result<()>; + fn set_storage, Option>)>>(&mut self, changes: I) -> error::Result<()>; /// Inject storage data into the database. fn reset_storage, Vec)>>(&mut self, iter: I) -> error::Result<()>; } diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs new file mode 100644 index 0000000000000..3ccc9debff5b4 --- /dev/null +++ b/substrate/client/src/client.rs @@ -0,0 +1,504 @@ +// 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::{self, block, AuthorityId}; +use primitives::block::Id as BlockId; +use primitives::storage::{StorageKey, StorageData}; +use runtime_support::Hashable; +use codec::{KeyedVec, Slicable}; +use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor}; + +use backend::{self, BlockImportOperation}; +use blockchain::{self, Info as ChainInfo, Backend as ChainBackend}; +use {error, in_mem, block_builder, runtime_io, bft}; + +/// 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, +} + +/// A header paired with a justification which has already been checked. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct JustifiedHeader { + header: block::Header, + justification: bft::Justification, +} + +impl JustifiedHeader { + /// Deconstruct the justified header into parts. + pub fn into_inner(self) -> (block::Header, bft::Justification) { + (self.header, self.justification) + } +} + +/// 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![]), None, 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(StorageData(self.state_at(id)? + .storage(&key.0)? + .ok_or_else(|| error::ErrorKind::NoValueForKey(key.0.clone()))? + .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::AuthLenEmpty)?).ok_or(error::ErrorKind::AuthLenInvalid)?) + .map(|i| state.storage(&i.to_keyed_vec(b":auth:")) + .map_err(|_| error::ErrorKind::Backend) + .and_then(|v| v.ok_or(error::ErrorKind::AuthEmpty(i))) + .and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::AuthInvalid(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) + } + + /// Check a header's justification. + pub fn check_justification( + &self, + header: block::Header, + justification: bft::UncheckedJustification, + ) -> error::Result { + let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?; + let just = bft::check_justification(&authorities[..], header.parent_hash, justification) + .map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.hash())))?; + Ok(JustifiedHeader { + header, + justification: just, + }) + } + + /// Queue a block for import. + pub fn import_block( + &self, + header: JustifiedHeader, + body: Option, + ) -> error::Result { + // TODO: import lock + // TODO: validate block + // TODO: import justification. + let (header, justification) = header.into_inner(); + 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; + trace!("Imported {}, (#{}), best={}", block::HeaderHash::from(header.blake2_256()), header.number, is_new_best); + transaction.set_block_data(header, body, Some(justification.uncheck().into()), 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) + } + + /// Get block justification set by id. + pub fn justification(&self, id: &BlockId) -> error::Result> { + self.backend.blockchain().justification(*id) + } +} + +impl bft::BlockImport for Client + where + B: backend::Backend, + E: state_machine::CodeExecutor, + error::Error: From<::Error> +{ + fn import_block(&self, block: block::Block, justification: bft::Justification) { + let justified_header = JustifiedHeader { + header: block.header, + justification, + }; + + let _ = self.import_block(justified_header, Some(block.transactions)); + } +} + +impl bft::Authorities for Client + where + B: backend::Backend, + E: state_machine::CodeExecutor, + error::Error: From<::Error> +{ + fn authorities(&self, at: &BlockId) -> Result, bft::Error> { + self.authorities_at(at).map_err(|_| bft::ErrorKind::StateUnavailable(*at).into()) + } +} + +#[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()) + } + + // since we are in the client module we can create falsely justified + // headers. + // TODO: remove this in favor of custom verification pipelines for the + // client + fn justify(header: &block::Header) -> bft::UncheckedJustification { + let hash = header.hash(); + let authorities = vec![ + Keyring::Alice.into(), + Keyring::Bob.into(), + Keyring::Charlie.into(), + ]; + + bft::UncheckedJustification { + digest: hash, + signatures: authorities.iter().map(|key| { + bft::sign_message( + bft::generic::Message::Commit(1, hash), + key, + header.parent_hash + ).signature + }).collect(), + round_number: 1, + } + } + + #[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 = genesis_config(); + + 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 = genesis_config(); + + 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(); + + let justification = justify(&block.header); + let justified = client.check_justification(block.header, justification).unwrap(); + client.import_block(justified, 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(); + + let justification = justify(&block.header); + let justified = client.check_justification(block.header, justification).unwrap(); + client.import_block(justified, 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/client/src/error.rs b/substrate/client/src/error.rs index 2ae07c870c59f..179fa7d910a91 100644 --- a/substrate/client/src/error.rs +++ b/substrate/client/src/error.rs @@ -18,6 +18,7 @@ use std; use state_machine; +use primitives::hexdisplay::HexDisplay; error_chain! { errors { @@ -45,23 +46,41 @@ error_chain! { display("Blockchain: {}", e), } - /// Bad justification for header. - BadJustification(h: ::primitives::block::Id) { - description("bad justification for header"), - display("bad justification for header: {}", h), + /// Attempt to read storage yet nothing set for that key. + NoValueForKey(key: Vec) { + description("storage doesn't contain key"), + display("Storage does not contain the key entry: {}", HexDisplay::from(key)), + } + + /// Invalid state data. + AuthLenEmpty { + description("authority count state error"), + display("Current state of blockchain has no authority count value"), + } + + /// Invalid state data. + AuthEmpty(i: u32) { + description("authority value state error"), + display("Current state of blockchain has no authority value for index {}", i), } /// Invalid state data. - AuthLen { + AuthLenInvalid { description("authority count state error"), display("Current state of blockchain has invalid authority count value"), } /// Invalid state data. - Auth(i: u32) { + AuthInvalid(i: u32) { description("authority value state error"), display("Current state of blockchain has invalid authority value for index {}", i), } + + /// Bad justification for header. + BadJustification(h: ::primitives::block::Id) { + description("bad justification for header"), + display("bad justification for header: {}", h), + } } } diff --git a/substrate/client/src/genesis.rs b/substrate/client/src/genesis.rs index fb3e228d03f1c..5008609777205 100644 --- a/substrate/client/src/genesis.rs +++ b/substrate/client/src/genesis.rs @@ -114,7 +114,7 @@ mod tests { } #[test] - fn construct_genesis_should_work() { + fn construct_genesis_should_work_with_native() { let mut storage = GenesisConfig::new_simple( vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 1000 ).genesis_map(); @@ -133,6 +133,19 @@ mod tests { "execute_block", &b1data ).unwrap(); + } + + #[test] + fn construct_genesis_should_work_with_wasm() { + let mut storage = GenesisConfig::new_simple( + vec![Keyring::One.to_raw_public(), Keyring::Two.to_raw_public()], 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 backend = InMemory::from(storage); + let (b1data, _b1hash) = block1(genesis_hash, &backend); let mut overlay = OverlayedChanges::default(); let _ = execute( diff --git a/substrate/client/src/in_mem.rs b/substrate/client/src/in_mem.rs index 62ef3b971552b..b6a0341169be4 100644 --- a/substrate/client/src/in_mem.rs +++ b/substrate/client/src/in_mem.rs @@ -180,7 +180,7 @@ impl backend::BlockImportOperation for BlockImportOperation { Ok(()) } - fn set_storage, Vec)>>(&mut self, changes: I) -> error::Result<()> { + fn set_storage, Option>)>>(&mut self, changes: I) -> error::Result<()> { self.pending_state.commit(changes); Ok(()) } diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index c86c06afef917..6054808f9b433 100644 --- a/substrate/client/src/lib.rs +++ b/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)] @@ -41,485 +41,7 @@ pub mod backend; pub mod in_mem; pub mod genesis; pub mod block_builder; +mod client; +pub use client::{Client, ClientInfo, CallResult, ImportResult, BlockStatus, new_in_mem}; pub use blockchain::Info as ChainInfo; - -use primitives::{block, AuthorityId}; -use primitives::block::Id as BlockId; -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}; -use runtime_support::Hashable; - -/// 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, -} - -/// A header paired with a justification which has already been checked. -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct JustifiedHeader { - header: block::Header, - justification: bft::Justification, -} - -impl JustifiedHeader { - /// Deconstruct the justified header into parts. - pub fn into_inner(self) -> (block::Header, bft::Justification) { - (self.header, self.justification) - } -} - -/// 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![]), None, 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) - } - - /// Check a header's justification. - pub fn check_justification( - &self, - header: block::Header, - justification: bft::UncheckedJustification, - ) -> error::Result { - let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?; - let just = bft::check_justification(&authorities[..], header.parent_hash, justification) - .map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.hash())))?; - Ok(JustifiedHeader { - header, - justification: just, - }) - } - - /// Queue a block for import. - pub fn import_block( - &self, - header: JustifiedHeader, - body: Option, - ) -> error::Result { - // TODO: import lock - // TODO: validate block - // TODO: import justification. - let (header, justification) = header.into_inner(); - 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; - trace!("Imported {}, (#{}), best={}", block::HeaderHash::from(header.blake2_256()), header.number, is_new_best); - transaction.set_block_data(header, body, Some(justification.uncheck().into()), 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) - } - - /// Get block justification set by id. - pub fn justification(&self, id: &BlockId) -> error::Result> { - self.backend.blockchain().justification(*id) - } -} - -impl bft::BlockImport for Client - where - B: backend::Backend, - E: state_machine::CodeExecutor, - error::Error: From<::Error> -{ - fn import_block(&self, block: block::Block, justification: bft::Justification) { - let justified_header = JustifiedHeader { - header: block.header, - justification, - }; - - let _ = self.import_block(justified_header, Some(block.transactions)); - } -} - -impl bft::Authorities for Client - where - B: backend::Backend, - E: state_machine::CodeExecutor, - error::Error: From<::Error> -{ - fn authorities(&self, at: &BlockId) -> Result, bft::Error> { - self.authorities_at(at).map_err(|_| bft::ErrorKind::StateUnavailable(*at).into()) - } -} - -#[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()) - } - - // since we are in the client module we can create falsely justified - // headers. - // TODO: remove this in favor of custom verification pipelines for the - // client - fn justify(header: &block::Header) -> bft::UncheckedJustification { - let hash = header.hash(); - let authorities = vec![ - Keyring::Alice.into(), - Keyring::Bob.into(), - Keyring::Charlie.into(), - ]; - - bft::UncheckedJustification { - digest: hash, - signatures: authorities.iter().map(|key| { - bft::sign_message( - bft::generic::Message::Commit(1, hash), - key, - header.parent_hash - ).signature - }).collect(), - round_number: 1, - } - } - - #[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 = genesis_config(); - - 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 = genesis_config(); - - 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(); - - let justification = justify(&block.header); - let justified = client.check_justification(block.header, justification).unwrap(); - client.import_block(justified, 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 = genesis_config(); - - 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(); - - let justification = justify(&block.header); - let justified = client.check_justification(block.header, justification).unwrap(); - client.import_block(justified, 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/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs index 00bdb55865644..4d525a5975387 100644 --- a/substrate/executor/src/wasm_executor.rs +++ b/substrate/executor/src/wasm_executor.rs @@ -171,34 +171,53 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, } this.ext.set_storage(key, value); }, + ext_clear_storage(key_data: *const u8, key_len: u32) => { + let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; + if let Some(preimage) = this.hash_lookup.get(&key) { + info!(target: "wasm-trace", "*** Clearing storage: %{} [k={}]", ascii_format(&preimage), HexDisplay::from(&key)); + } else { + info!(target: "wasm-trace", "*** Clearing storage: {} [k={}]", ascii_format(&key), HexDisplay::from(&key)); + } + this.ext.clear_storage(&key); + }, + // return 0 and place u32::max_value() into written_out if no value exists for the key. ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; - let value = this.ext.storage(&key).map_err(|_| DummyUserError)?; + let maybe_value = this.ext.storage(&key); if let Some(preimage) = this.hash_lookup.get(&key) { - info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), HexDisplay::from(&value), HexDisplay::from(&key)); + info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "".to_owned() }, HexDisplay::from(&key)); } else { - info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key)); + info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "".to_owned() }, HexDisplay::from(&key)); } - let offset = this.heap.allocate(value.len() as u32) as u32; - this.memory.set(offset, &value).map_err(|_| DummyUserError)?; - - this.memory.write_primitive(written_out, value.len() as u32)?; - offset + if let Some(value) = maybe_value { + let offset = this.heap.allocate(value.len() as u32) as u32; + this.memory.set(offset, &value).map_err(|_| DummyUserError)?; + this.memory.write_primitive(written_out, value.len() as u32)?; + offset + } else { + this.memory.write_primitive(written_out, u32::max_value())?; + 0 + } }, + // return u32::max_value() if no value exists for the key. ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => { let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?; - let value = this.ext.storage(&key).map_err(|_| DummyUserError)?; + let maybe_value = this.ext.storage(&key); if let Some(preimage) = this.hash_lookup.get(&key) { - info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), HexDisplay::from(&value), HexDisplay::from(&key)); + info!(target: "wasm-trace", " Getting storage: %{} == {} [k={}]", ascii_format(&preimage), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "".to_owned() }, HexDisplay::from(&key)); + } else { + info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), if let Some(ref b) = maybe_value { format!("{}", HexDisplay::from(b)) } else { "".to_owned() }, HexDisplay::from(&key)); + } + if let Some(value) = maybe_value { + let value = &value[value_offset as usize..]; + let written = ::std::cmp::min(value_len as usize, value.len()); + this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?; + written as u32 } else { - info!(target: "wasm-trace", " Getting storage: {} == {} [k={}]", ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key)); + u32::max_value() } - let value = &value[value_offset as usize..]; - let written = ::std::cmp::min(value_len as usize, value.len()); - this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?; - written as u32 }, ext_storage_root(result: *mut u8) => { let r = this.ext.storage_root(); @@ -369,7 +388,7 @@ mod tests { b"foo".to_vec() => b"bar".to_vec(), b"baz".to_vec() => b"bar".to_vec() ]; - assert_eq!(expected, ext.storage); + assert_eq!(expected, ext); } #[test] diff --git a/substrate/executor/wasm/src/lib.rs b/substrate/executor/wasm/src/lib.rs index 11401a39d7b45..013ecca1ed7a8 100644 --- a/substrate/executor/wasm/src/lib.rs +++ b/substrate/executor/wasm/src/lib.rs @@ -19,7 +19,7 @@ impl_stubs!( set_storage(b"input", input); print("storage"); - let foo = storage(b"foo"); + let foo = storage(b"foo").unwrap(); print("set_storage"); set_storage(b"baz", &foo); diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index d5fd4dfb021bc..cbc81694c0999 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm index 2623dc31b8a59..ab23e9b4f7a2a 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/rpc/src/state/tests.rs b/substrate/rpc/src/state/tests.rs index 469b794b05899..9bae8b1cd5610 100644 --- a/substrate/rpc/src/state/tests.rs +++ b/substrate/rpc/src/state/tests.rs @@ -35,7 +35,7 @@ fn should_return_storage() { assert_matches!( StateApi::storage(&client, StorageKey(vec![10]), genesis_hash), - Ok(ref x) if x.0.is_empty() + Err(Error(ErrorKind::Client(client::error::ErrorKind::NoValueForKey(ref k)), _)) if *k == vec![10] ) } diff --git a/substrate/runtime-io/with_std.rs b/substrate/runtime-io/with_std.rs index 98864baa63be7..5d785119c561e 100644 --- a/substrate/runtime-io/with_std.rs +++ b/substrate/runtime-io/with_std.rs @@ -27,45 +27,45 @@ pub extern crate substrate_codec as codec; // re-export hashing functions. pub use primitives::{blake2_256, twox_128, twox_256}; -pub use substrate_state_machine::{Externalities, ExternalitiesError, TestExternalities}; +pub use substrate_state_machine::{Externalities, TestExternalities}; use primitives::hexdisplay::HexDisplay; // TODO: use the real error, not NoError. -environmental!(ext : trait Externalities); +environmental!(ext: trait Externalities); /// Get `key` from storage and return a `Vec`, empty if there's a problem. -pub fn storage(key: &[u8]) -> Vec { - ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec())) +pub fn storage(key: &[u8]) -> Option> { + ext::with(|ext| ext.storage(key).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 beyond the offset. -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) - 0 - } - }).expect("read_storage cannot be called outside of an Externalities-provided environment.") +/// the number of bytes that the key in storage was beyond the offset or None if the storage entry +/// doesn't exist at all. +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { + ext::with(|ext| ext.storage(key).map(|value| { + 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() + })).expect("read_storage cannot be called outside of an Externalities-provided environment.") } -/// Set the storage to some particular key. +/// Set the storage of some particular key to Some value. pub fn set_storage(key: &[u8], value: &[u8]) { ext::with(|ext| ext.set_storage(key.to_vec(), value.to_vec()) ); } +/// Clear the storage of some particular key. +pub fn clear_storage(key: &[u8]) { + ext::with(|ext| + ext.clear_storage(key) + ); +} + /// The current relay chain identifier. pub fn chain_id() -> u64 { ext::with(|ext| @@ -164,37 +164,37 @@ mod std_tests { #[test] fn storage_works() { - let mut t = TestExternalities { storage: map![], }; + let mut t = TestExternalities::new(); assert!(with_externalities(&mut t, || { - assert_eq!(storage(b"hello"), b"".to_vec()); + assert_eq!(storage(b"hello"), None); set_storage(b"hello", b"world"); - assert_eq!(storage(b"hello"), b"world".to_vec()); - assert_eq!(storage(b"foo"), b"".to_vec()); + assert_eq!(storage(b"hello"), Some(b"world".to_vec())); + assert_eq!(storage(b"foo"), None); set_storage(b"foo", &[1, 2, 3][..]); true })); - t.storage = map![b"foo".to_vec() => b"bar".to_vec()]; + t = 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()); + assert_eq!(storage(b"hello"), None); + assert_eq!(storage(b"foo"), Some(b"bar".to_vec())); false })); } #[test] fn read_storage_works() { - let mut t = TestExternalities { storage: map![ + let mut t: TestExternalities = 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!(read_storage(b":test", &mut v[..], 0).unwrap() >= 4); assert_eq!(v, [11u8, 0, 0, 0]); let mut w = [0u8; 11]; - assert!(read_storage(b":test", &mut w[..], 4) >= 11); + assert!(read_storage(b":test", &mut w[..], 4).unwrap() >= 11); assert_eq!(&w, b"Hello world"); }); } diff --git a/substrate/runtime-io/without_std.rs b/substrate/runtime-io/without_std.rs index cac6b6dff2192..ad31020cc4962 100644 --- a/substrate/runtime-io/without_std.rs +++ b/substrate/runtime-io/without_std.rs @@ -43,6 +43,7 @@ extern "C" { 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_clear_storage(key_data: *const u8, key_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_storage_root(result: *mut u8); @@ -55,11 +56,15 @@ extern "C" { } /// Get `key` from storage and return a `Vec`, empty if there's a problem. -pub fn storage(key: &[u8]) -> Vec { +pub fn storage(key: &[u8]) -> Option> { 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) + if length == u32::max_value() { + None + } else { + Some(Vec::from_raw_parts(ptr, length as usize, length as usize)) + } } } @@ -73,15 +78,27 @@ pub fn set_storage(key: &[u8], value: &[u8]) { } } +/// Set the storage to some particular key. +pub fn clear_storage(key: &[u8]) { + unsafe { + ext_clear_storage( + key.as_ptr(), key.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 beyond the offset. -pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize { +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { unsafe { - ext_get_storage_into( + match 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 + ) { + none if none == u32::max_value() => None, + length => Some(length as usize), + } } } diff --git a/substrate/runtime-support/src/storage.rs b/substrate/runtime-support/src/storage.rs index 9896cb07c61ed..7dbfd8013b35e 100644 --- a/substrate/runtime-support/src/storage.rs +++ b/substrate/runtime-support/src/storage.rs @@ -18,14 +18,34 @@ use rstd::prelude::*; use runtime_io::{self, twox_128}; -use codec::{Slicable, KeyedVec}; +use codec::{Slicable, KeyedVec, Input}; // TODO: consider using blake256 to avoid possible preimage attack. -/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. +struct IncrementalInput<'a> { + key: &'a [u8], + pos: usize, +} + +impl<'a> Input for IncrementalInput<'a> { + fn read(&mut self, into: &mut [u8]) -> usize { + let len = runtime_io::read_storage(self.key, into, self.pos).unwrap_or(0); + let read = ::rstd::cmp::min(len, into.len()); + self.pos += read; + read + } +} + + /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { - let raw = runtime_io::storage(&twox_128(key)[..]); - Slicable::decode(&mut &raw[..]) + let key = twox_128(key); + runtime_io::read_storage(&key[..], &mut [0; 0][..], 0).map(|_| { + let mut input = IncrementalInput { + key: &key[..], + pos: 0, + }; + Slicable::decode(&mut input).expect("stroage is not null, therefore must be a valid type") + }) } /// Return the value of the item in storage under `key`, or the type's default if there is no @@ -85,17 +105,17 @@ pub fn take_or_else T>(key: &[u8], default_v /// Check to see if `key` has an explicit entry in storage. pub fn exists(key: &[u8]) -> bool { - let mut x = [0u8; 1]; - runtime_io::read_storage(&twox_128(key)[..], &mut x[..], 0) >= 1 + let mut x = [0u8; 0]; + runtime_io::read_storage(&twox_128(key)[..], &mut x[..], 0).is_some() } /// Ensure `key` has no explicit entry in storage. pub fn kill(key: &[u8]) { - runtime_io::set_storage(&twox_128(key)[..], b""); + runtime_io::clear_storage(&twox_128(key)[..]); } /// Get a Vec of bytes from storage. -pub fn get_raw(key: &[u8]) -> Vec { +pub fn get_raw(key: &[u8]) -> Option> { runtime_io::storage(&twox_128(key)[..]) } @@ -127,12 +147,18 @@ pub trait StorageVec { } } + fn clear_item(index: u32) { + if index < Self::count() { + kill(&index.to_keyed_vec(Self::PREFIX)); + } + } + fn item(index: u32) -> Self::Item { get_or_default(&index.to_keyed_vec(Self::PREFIX)) } fn set_count(count: u32) { - (count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default())); + (count..Self::count()).for_each(Self::clear_item); put(&b"len".to_keyed_vec(Self::PREFIX), &count); } @@ -142,12 +168,17 @@ pub trait StorageVec { } pub mod unhashed { - use super::{runtime_io, Slicable, KeyedVec, Vec}; + use super::{runtime_io, Slicable, KeyedVec, Vec, IncrementalInput}; /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { - let raw = runtime_io::storage(key); - T::decode(&mut &raw[..]) + runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| { + let mut input = IncrementalInput { + key, + pos: 0, + }; + Slicable::decode(&mut input).expect("stroage is not null, therefore must be a valid type") + }) } /// Return the value of the item in storage under `key`, or the type's default if there is no @@ -207,17 +238,16 @@ pub mod unhashed { /// Check to see if `key` has an explicit entry in storage. pub fn exists(key: &[u8]) -> bool { - let mut x = [0u8; 1]; - runtime_io::read_storage(key, &mut x[..], 0) >= 1 + runtime_io::read_storage(key, &mut [0;0][..], 0).is_some() } /// Ensure `key` has no explicit entry in storage. pub fn kill(key: &[u8]) { - runtime_io::set_storage(key, b""); + runtime_io::clear_storage(key); } /// Get a Vec of bytes from storage. - pub fn get_raw(key: &[u8]) -> Vec { + pub fn get_raw(key: &[u8]) -> Option> { runtime_io::storage(key) } @@ -249,12 +279,18 @@ pub mod unhashed { } } + fn clear_item(index: u32) { + if index < Self::count() { + kill(&index.to_keyed_vec(Self::PREFIX)); + } + } + fn item(index: u32) -> Self::Item { get_or_default(&index.to_keyed_vec(Self::PREFIX)) } fn set_count(count: u32) { - (count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default())); + (count..Self::count()).for_each(Self::clear_item); put(&b"len".to_keyed_vec(Self::PREFIX), &count); } @@ -267,13 +303,12 @@ pub mod unhashed { #[cfg(test)] mod tests { use super::*; - use std::collections::HashMap; use primitives::hexdisplay::HexDisplay; use runtime_io::{storage, twox_128, TestExternalities, with_externalities}; #[test] fn integers_can_be_stored() { - let mut t = TestExternalities { storage: HashMap::new(), }; + let mut t = TestExternalities::new(); with_externalities(&mut t, || { let x = 69u32; put(b":test", &x); @@ -290,7 +325,7 @@ mod tests { #[test] fn bools_can_be_stored() { - let mut t = TestExternalities { storage: HashMap::new(), }; + let mut t = TestExternalities::new(); with_externalities(&mut t, || { let x = true; put(b":test", &x); @@ -308,11 +343,11 @@ mod tests { #[test] fn vecs_can_be_retrieved() { - let mut t = TestExternalities { storage: HashMap::new(), }; + let mut t = TestExternalities::new(); with_externalities(&mut t, || { runtime_io::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world"); let x = b"Hello world".to_vec(); - println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")))); + println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")).unwrap())); let y = get::>(b":test").unwrap(); assert_eq!(x, y); @@ -321,7 +356,7 @@ mod tests { #[test] fn vecs_can_be_stored() { - let mut t = TestExternalities { storage: HashMap::new(), }; + let mut t = TestExternalities::new(); let x = b"Hello world".to_vec(); with_externalities(&mut t, || { @@ -330,7 +365,7 @@ mod tests { println!("Ext is {:?}", t); with_externalities(&mut t, || { - println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")))); + println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test")).unwrap())); let y: Vec = get(b":test").unwrap(); assert_eq!(x, y); }); diff --git a/substrate/state-machine/src/backend.rs b/substrate/state-machine/src/backend.rs index f77e9a59d811d..2c019510ffcb9 100644 --- a/substrate/state-machine/src/backend.rs +++ b/substrate/state-machine/src/backend.rs @@ -17,11 +17,7 @@ //! State machine backends. These manage the code and storage of contracts. use std::{error, fmt}; - -use super::{Update, MemoryState}; - -/// Output of a commit. -pub struct Committed {} +use std::collections::HashMap; /// A state backend is used to read state data and can have changes committed /// to it. @@ -29,12 +25,12 @@ pub trait Backend { /// An error type when fetching data is not possible. type Error: super::Error; - /// Get keyed storage associated with specific address. - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; + /// Get keyed storage associated with specific address, or None if there is nothing associated. + fn storage(&self, key: &[u8]) -> Result, Self::Error>; /// Commit updates to the backend and get new state. - fn commit(&mut self, changes: I) -> Committed - where I: IntoIterator; + fn commit(&mut self, changes: I) + where I: IntoIterator, Option>)>; /// Get all key/value pairs into a Vec. fn pairs(&self) -> Vec<(&[u8], &[u8])>; @@ -57,38 +53,28 @@ impl error::Error for Void { /// In-memory backend. Fully recomputes tries on each commit but useful for /// tests. -#[derive(Debug, PartialEq, Default, Clone)] -pub struct InMemory { - inner: MemoryState, // keeps all the state in memory. -} - -impl InMemory { - /// Create a new instance from a given storage map. - pub fn from(storage: ::std::collections::HashMap, Vec>) -> Self { - InMemory { - inner: MemoryState { - storage - } - } - } -} +pub type InMemory = HashMap, Vec>; impl Backend for InMemory { type Error = Void; - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { - Ok(self.inner.storage(key).unwrap_or(&[])) + fn storage(&self, key: &[u8]) -> Result, Self::Error> { + Ok(self.get(key).map(AsRef::as_ref)) } - fn commit(&mut self, changes: I) -> Committed - where I: IntoIterator + fn commit(&mut self, changes: I) + where I: IntoIterator, Option>)> { - self.inner.update(changes); - Committed {} + for (key, val) in changes { + match val { + Some(v) => { self.insert(key, v); }, + None => { self.remove(&key); }, + } + } } fn pairs(&self) -> Vec<(&[u8], &[u8])> { - self.inner.storage.iter().map(|(k, v)| (&k[..], &v[..])).collect() + self.iter().map(|(k, v)| (&k[..], &v[..])).collect() } } diff --git a/substrate/state-machine/src/ext.rs b/substrate/state-machine/src/ext.rs index e0176ad41caae..ac047a31c303c 100644 --- a/substrate/state-machine/src/ext.rs +++ b/substrate/state-machine/src/ext.rs @@ -17,9 +17,10 @@ //! Conrete externalities implementation. use std::{error, fmt}; +use std::collections::HashMap; use triehash::trie_root; use backend::Backend; -use {Externalities, ExternalitiesError, OverlayedChanges}; +use {Externalities, OverlayedChanges}; /// Errors that can occur when interacting with the externalities. #[derive(Debug, Copy, Clone)] @@ -58,17 +59,29 @@ pub struct Ext<'a, B: 'a> { pub backend: &'a B, } +#[cfg(test)] +impl<'a, B: 'a + Backend> Ext<'a, B> { + pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { + self.backend.pairs().iter() + .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) + .chain(self.overlay.committed.clone().into_iter()) + .chain(self.overlay.prospective.clone().into_iter()) + .collect::>() + .into_iter() + .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) + .collect() + } +} + impl<'a, B: 'a> Externalities for Ext<'a, B> where B: Backend { - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { - match self.overlay.storage(key) { - Some(x) => Ok(x), - None => self.backend.storage(key).map_err(|_| ExternalitiesError), - } + fn storage(&self, key: &[u8]) -> Option<&[u8]> { + self.overlay.storage(key).unwrap_or_else(|| + self.backend.storage(key).expect("Externalities not allowed to fail within runtime")) } - fn set_storage(&mut self, key: Vec, value: Vec) { + fn place_storage(&mut self, key: Vec, value: Option>) { self.overlay.set_storage(key, value); } @@ -77,13 +90,12 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> } fn storage_root(&self) -> [u8; 32] { - let mut all_pairs = self.backend.pairs(); - all_pairs.extend( - self.overlay.committed.storage.iter() - .chain(self.overlay.prospective.storage.iter()) - .map(|(k, v)| (&k[..], &v[..])) - ); - - trie_root(all_pairs.into_iter().map(|(k, v)| (k.to_vec(), v.to_vec()))).0 + trie_root(self.backend.pairs().iter() + .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) + .chain(self.overlay.committed.clone().into_iter()) + .chain(self.overlay.prospective.clone().into_iter()) + .collect::>() + .into_iter() + .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))).0 } } diff --git a/substrate/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs index a3f2e3dc4b898..363f29383039a 100644 --- a/substrate/state-machine/src/lib.rs +++ b/substrate/state-machine/src/lib.rs @@ -32,6 +32,7 @@ extern crate triehash; extern crate byteorder; use std::collections::HashMap; +use std::collections::hash_map::Drain; use std::fmt; pub mod backend; @@ -40,35 +41,7 @@ mod testing; pub use testing::TestExternalities; pub use ext::Ext; - -/// Updates to be committed to the state. -pub type Update = (Vec, Vec); - -// in-memory section of the state. -#[derive(Debug, PartialEq, Default, Clone)] -struct MemoryState { - storage: HashMap, Vec>, -} - -impl MemoryState { - fn storage(&self, key: &[u8]) -> Option<&[u8]> { - self.storage.get(key).map(|v| &v[..]) - } - - fn set_storage(&mut self, key: Vec, val: Vec) { - self.storage.insert(key, val); - } - - fn update(&mut self, changes: I) where I: IntoIterator { - for (key, val) in changes { - if val.is_empty() { - self.storage.remove(&key); - } else { - self.storage.insert(key, val); - } - } - } -} +pub use backend::Backend; /// The overlayed changes to state to be queried on top of the backend. /// @@ -76,34 +49,41 @@ impl MemoryState { /// that can be cleared. #[derive(Default)] pub struct OverlayedChanges { - prospective: MemoryState, - committed: MemoryState, + prospective: HashMap, Option>>, + committed: HashMap, Option>>, } impl OverlayedChanges { - fn storage(&self, key: &[u8]) -> Option<&[u8]> { - self.prospective.storage(key) - .or_else(|| self.committed.storage(key)) - .and_then(|v| if v.is_empty() { None } else { Some(v) }) + /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered + /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose + /// value has been set. + pub fn storage(&self, key: &[u8]) -> Option> { + self.prospective.get(key) + .or_else(|| self.committed.get(key)) + .map(|x| x.as_ref().map(AsRef::as_ref)) } - fn set_storage(&mut self, key: Vec, val: Vec) { - self.prospective.set_storage(key, val); + fn set_storage(&mut self, key: Vec, val: Option>) { + self.prospective.insert(key, val); } /// Discard prospective changes to state. pub fn discard_prospective(&mut self) { - self.prospective.storage.clear(); + self.prospective.clear(); } /// Commit prospective changes to state. pub fn commit_prospective(&mut self) { - self.committed.update(self.prospective.storage.drain()); + if self.committed.is_empty() { + ::std::mem::swap(&mut self.prospective, &mut self.committed); + } else { + self.committed.extend(self.prospective.drain()); + } } /// Drain prospective changes to an iterator. - pub fn drain(&mut self) -> ::std::collections::hash_map::Drain, std::vec::Vec> { - self.committed.storage.drain() + pub fn drain(&mut self) -> Drain, Option>> { + self.committed.drain() } } @@ -119,19 +99,32 @@ impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} /// would not be executed unless externalities were available. This is included for completeness, /// and as a transition away from the pre-existing framework. #[derive(Debug, Eq, PartialEq)] -pub struct ExternalitiesError; +pub enum ExecutionError { + /// The entry `:code` doesn't exist in storage so there's no way we can execute anything. + CodeEntryDoesNotExist +} -impl fmt::Display for ExternalitiesError { +impl fmt::Display for ExecutionError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Externalities Error") } } /// Externalities: pinned to specific active address. pub trait Externalities { /// Read storage of current contract being called. - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError>; + fn storage(&self, key: &[u8]) -> Option<&[u8]>; + + /// Set storage entry `key` of current contract being called (effective immediately). + fn set_storage(&mut self, key: Vec, value: Vec) { + self.place_storage(key, Some(value)); + } - /// Set storage of current contract being called (effective immediately). - fn set_storage(&mut self, key: Vec, value: Vec); + /// Clear a storage entry (`key`) of current contract being called (effective immediately). + fn clear_storage(&mut self, key: &[u8]) { + self.place_storage(key.to_vec(), None); + } + + /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). + fn place_storage(&mut self, key: Vec, value: Option>); /// Get the identity of the chain. fn chain_id(&self) -> u64; @@ -167,7 +160,8 @@ pub fn execute( exec: &Exec, method: &str, call_data: &[u8], -) -> Result, Box> { +) -> Result, Box> +{ let result = { let mut externalities = ext::Ext { @@ -175,7 +169,9 @@ pub fn execute( overlay: &mut *overlay }; // make a copy. - let code = externalities.storage(b":code").unwrap_or(&[]).to_vec(); + let code = externalities.storage(b":code") + .ok_or(Box::new(ExecutionError::CodeEntryDoesNotExist) as Box)? + .to_vec(); exec.call( &mut externalities, @@ -211,21 +207,24 @@ mod tests { assert!(overlayed.storage(&key).is_none()); - overlayed.set_storage(key.clone(), vec![1, 2, 3]); - assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); + overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); overlayed.commit_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - overlayed.set_storage(key.clone(), vec![]); - assert!(overlayed.storage(&key).is_none()); + overlayed.set_storage(key.clone(), Some(vec![])); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); + + overlayed.set_storage(key.clone(), None); + assert!(overlayed.storage(&key).unwrap().is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - overlayed.set_storage(key.clone(), vec![]); + overlayed.set_storage(key.clone(), None); overlayed.commit_prospective(); - assert!(overlayed.storage(&key).is_none()); + assert!(overlayed.storage(&key).unwrap().is_none()); } macro_rules! map { @@ -239,16 +238,19 @@ mod tests { let mut backend = InMemory::from(map![ b"doe".to_vec() => b"reindeer".to_vec(), b"dog".to_vec() => b"puppyXXX".to_vec(), - b"dogglesworth".to_vec() => b"catXXX".to_vec() + b"dogglesworth".to_vec() => b"catXXX".to_vec(), + b"doug".to_vec() => b"notadog".to_vec() ]); let mut overlay = OverlayedChanges { - committed: MemoryState { storage: map![ - b"dog".to_vec() => b"puppy".to_vec(), - b"dogglesworth".to_vec() => b"catYYY".to_vec() - ], }, - prospective: MemoryState { storage: map![ - b"dogglesworth".to_vec() => b"cat".to_vec() - ], }, + committed: map![ + b"dog".to_vec() => Some(b"puppy".to_vec()), + b"dogglesworth".to_vec() => Some(b"catYYY".to_vec()), + b"doug".to_vec() => Some(vec![]) + ], + prospective: map![ + b"dogglesworth".to_vec() => Some(b"cat".to_vec()), + b"doug".to_vec() => None + ], }; let ext = Ext { backend: &mut backend, diff --git a/substrate/state-machine/src/testing.rs b/substrate/state-machine/src/testing.rs index a85e1d3f4a2de..6d991a9acab5b 100644 --- a/substrate/state-machine/src/testing.rs +++ b/substrate/state-machine/src/testing.rs @@ -17,38 +17,28 @@ //! Test implementation for Externalities. use std::collections::HashMap; -use super::{Externalities, ExternalitiesError}; +use super::Externalities; use triehash::trie_root; /// Simple HashMap based Externalities impl. -#[derive(Debug, Default)] -pub struct TestExternalities { - /// The storage. - pub storage: HashMap, Vec>, -} - -impl TestExternalities { - /// Create a new instance with empty storage. - pub fn new() -> Self { - TestExternalities { - storage: HashMap::new(), - } - } -} +pub type TestExternalities = HashMap, Vec>; 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 storage(&self, key: &[u8]) -> Option<&[u8]> { + self.get(key).map(AsRef::as_ref) } - fn set_storage(&mut self, key: Vec, value: Vec) { - self.storage.insert(key, value); + fn place_storage(&mut self, key: Vec, maybe_value: Option>) { + match maybe_value { + Some(value) => { self.insert(key, value); } + None => { self.remove(&key); } + } } fn chain_id(&self) -> u64 { 42 } fn storage_root(&self) -> [u8; 32] { - trie_root(self.storage.clone()).0 + trie_root(self.clone()).0 } } diff --git a/substrate/test-runtime/src/system.rs b/substrate/test-runtime/src/system.rs index 4c98e16003c51..a6de75760a475 100644 --- a/substrate/test-runtime/src/system.rs +++ b/substrate/test-runtime/src/system.rs @@ -136,14 +136,14 @@ mod tests { use ::{Header, Digest, Transaction, UncheckedTransaction}; fn new_test_ext() -> TestExternalities { - TestExternalities { storage: map![ + map![ twox_128(b"latest").to_vec() => vec![69u8; 32], twox_128(b":auth:len").to_vec() => vec![].and(&3u32), twox_128(&0u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Alice.to_raw_public().to_vec(), twox_128(&1u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Bob.to_raw_public().to_vec(), twox_128(&2u32.to_keyed_vec(b":auth:")).to_vec() => Keyring::Charlie.to_raw_public().to_vec(), twox_128(&Keyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ], } + ] } fn construct_signed_tx(tx: Transaction) -> UncheckedTransaction { diff --git a/substrate/test-runtime/wasm/genesis.wasm b/substrate/test-runtime/wasm/genesis.wasm index 3eca5047352e7..7daba8a15c586 100644 Binary files a/substrate/test-runtime/wasm/genesis.wasm and b/substrate/test-runtime/wasm/genesis.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index c8ac9359ef120..5007dd36a299a 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm index 3329885da9a60..a57d2f1fd4ead 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm differ